TDS: The package builder failed. Please see build output log for more details

In our Sitecore team, we use Team Development for Sitecore (TDS) by Hedgehog Development to sync out Sitecore items to/from a TDS project/Sitecore dev site. It’s a pretty useful addon, but it has some weaknesses. One problem encountered with using it is a package build failure. When we build our QA or Authoring packages, sometimes TDS will fail to build the package with the error:

“The package builder failed. Please see build output log for more details.”

Once I had gotten this error repeatedly, I contacted the TDS support team. At first they just suggested to clean and rebuild the solution, which worked occasionally, but was definitely not a real fix for the problem. So I followed up and they gave some helpful advice.

First, TDS packages can be huge, especially if you opt to include both code and Sitecore items in the packages. This can be done by going to TDS.Master properties -> Update Package tab -> Package Generation Options and select "Generate separate code and item packages". We never deploy Sitecore items via packages (we use Razl, another Hedgehog product) so we opted out of that early on to decrease the package size. That reduced our sizes to around 150-165K on average.

Next, TDS suggested that we disable concurrent project builds. We were able to do so by navigating to Tools -> Options -> Projects and Solutions -> Build and Run in VS and changing the "maximum number of parallel project builds" to 1. Make sure to save the solution and restart VS before attempting to build a package again.

Finally, we decided to lower the package size in other ways. We opened our Configuration Manager and unchecked Build on some projects that did not need to be built every time. For example, I believe there 3 or 4 projects that we did not need to build for QA packages, so we unchecked those, closed the window and committed the solution change.

Since we’ve made these changes and merged them to the branches and trunks that need them, we haven’t seen this error crop up much at all. I hope this helps other TDS users.

Razor Template Scripting with jQuery

Recently I discovered template scripting in Razor (not sure what the actual name for it is) and it’s really saved me in some recent projects.  It’s basically a markup template/placeholder inside of a script tag, with dynamic references to model attributes. This lets you render new content onto your page using jQuery’s .render() and .appendTo() functions by targeting the ID of the template.

It starts with a controller action or method. You’ll need to return an instance of the model you’re using or a generic list of instances, depending on your situation. In my case, I made my controller action return a list of items to be added to a grid, if the user clicked a button indicating they wanted to see more items.

Some very basic code:

public ActionResult GetItems(type param1, type param2)
 {
    var itemList = new List<MyModel>();
    itemList = GetNewContent(param1, param2);
    if (itemList.Count < 1)
    {
       return Json(new { success = false }, JsonRequestBehavior.AllowGet);
    }
    return Json(new { success = true, nextItems = itemList }, JsonRequestBehavior.AllowGet);
 }

 The markup looks a little like this:

2016-03-16 14_19_27-SubZero - Microsoft Visual Studio (Administrator).png

Notice the dynamic variables in purple – those are your model attributes, which should exist in the model objects you returned in the controller action.

The JS call:

$('#gridItemTemplate').render(data.nextItems).appendTo('div#subItemGrid');

Because the type of data.nextItems is a list of those model objects, this code knows what those attributes are and how to get them. This will then append the new data based on the template provided, to the element you specify – in my case, a div.

This is a very useful method for rendering markup on the fly using jQuery.

 

Creating a Sitecore Dictionary Parser in Javascript

If you have ever worked on a multi-language site, you know the difficulty of having to make sure every last word is translated correctly. Most of this text may be on your views or web pages in which it is relatively easy to translate. However, when we write scripts for website we implement validation in them, which creates more static text that needs to be translated.

I eventually ran into a situation just like this, where the client requested that pretty much everything be translated correctly depending on the current site language. So I was stuck – how do I grab the value of the French version of a Sitecore item somewhere inside the CMS, from Javascript?

It starts with JSON, which is no surprise. Essentially you need to create a JSON dictionary in a controller method and get all the dictionary items you need, depending on the current site language, then serve that to the view in a tag, which your JS can easily read and interpret. Let’s look at some code.

Step 1: Create a DictionaryItem class (optional).

In my example, I created a DictionaryItem class to make creating the list of dictionary items easier.

public class DictionaryItem
{
   public string Key { get; set; }
   public string Phrase { get; set; }

   public DictionaryItem()
   {
   }

   public DictionaryItem(string key, string phrase)
   {
      Key = key;
      Phrase = phrase;
   }
}

Step 2: Create your controller method.

In your controller, create a public method with a return value of string. It might look something like this:

public string GetJsonDictionary()
{
   // Get a list of all dictionary items.
   var db = Database.GetDatabase(EnvironmentHelper.IsProduction ? "pub" : "web");
   var dictionaryFolder = db.GetItem("your sitecore item id as string");
   var dictionaryItems = dictionaryFolder.Axes.GetDescendants().ToList();
   var condensedDictionaryItemList = new List();

   foreach (var dictionaryItem in dictionaryItems)
   {
      var dict = new DictionaryItem();

      // Get the field value of the item based on the item ID - your implementation may differ
      dict.Key = SitecoreApiHelper.GetFieldValue(dictionaryItem.ID.ToString(), "Key");

      // Get the dictionary item from Sitecore matching dict.Key.
      dict.Phrase = GetDictionaryItem(dict.Key);
      condensedDictionaryItemList.Add(dict);
   }
   var result = JsonConvert.SerializeObject(condensedDictionaryItemList);
   return result;
}

Step 3: Call the controller method from your view.

I dynamically created an object ‘window.ncpDictionary’ for this purpose. In the Html.Action() call, the first parameter is the method name, the second, the controller name (minus “Controller”).

Make sure to make this call before any other calls that depend on it.

@using (Html.BeginScripts())
{
   window.ncpDictionary = @Html.Action("GetJsonDictionary", "NCP");
}

Step 4: Create a Javascript function to get a phrase from the dictionary given the key (and any parameters (optional)). I created a new script for shared functions across an entire section of the project, but this is optional.

function getDictionaryPhrase(key, parameter) { 
   var value = ""; 
   parameter = parameter || ""; 
   if (window.ncpDictionary != null) { 
      window.ncpDictionary.forEach(function (element) { 
         if (element.Key == key) { 
            // TODO: Enhance later to allow multiple parameters. 
            value = element.Phrase.replace('{0}', parameter);
         }
      }); 
   } 
   if (value == "") { return key; } return value; 
}

This code iterates through the dictionary we created in the controller method and checks for the supplied key to the function. If it finds it, send back the phrase (value). You may include a parameter as well; in this case, if your dictionary phrase includes {0}, this will replace it with the supplied parameter, if any.

Step 5: The function call!

Here is an example:

getDictionaryPhrase('please-enter-a-valid-day-01-31'))

Just like that, you’ve created a parser that you can use throughout your website to make sure everything gets translated nicely. Happy Sitecoring.

The breakpoint will not currently be hit. The source code is different from the original version

If you debug web applications frequently, you probably see this message in Visual Studio a lot. You set a breakpoint and attach to process, but the breakpoint is not active. If you hover over it, you will see this message:

The breakpoint will not currently be hit. The source code is different from the original version.

To begin, let me explain why you got this message.

You made a change to a method, view, class, etc, but you didn’t build the project before attaching to w3wp.exe. That’s all. Note, it’s best to just use ‘Build Solution‘ rather than ‘Rebuild Solution’. I’ve had times where I tried a Rebuild but nothing changed. I can’t quite explain it as anything other than a VS oddity.

Alternatively, you may get this message if you did build the project successfully. In that case, go to your web application and run the method/view, and VS should activate the breakpoints for you. In other words, sometimes you need to begin execution for VS to act correctly.

Now, onto that second part of the message…

To allow the breakpoint to be hit when the source code is different, right-click on the breakpoint, choose ‘Location…’, and turn on ‘Allow the source code to be different from the original version’.

Danger-sign-B

Do NOT allow the source code to be different from the original version.

There may be some weird situation where you actually want it to be different, but I don’t know of one. This is kind of like in VS 2010, where the build fails, and VS asks you if you want to run the application anyway… same weirdness level.

If you DO set allow the source code to be different, your breakpoints will be active, but they won’t highlight the entire line.. causing all kinds of debugger oddities and bugs.

Take this example…

not fully

Sure, your breakpoint works now, but it’s not going to run anything past ‘products’, which causes an exception in the debugger. This can get even more weird as you continue debugging. Oh, and building won’t do a thing.

So, things to remember:

  • Always build before attaching
  • Never allow the source code to be different

 

Happy Debugging.

How to Alphabetize the Selected List of a Field in Sitecore

This is a continuation of my previous blog post, in which explained how to remove Sitecore list field icons (up/down, etc). The project I was working on was a two-part request, and this post will explain the first part and how it was handled.

Client wanted a field on their item to be sorted alphabetically (technically the Selected list of that field). This is best handled by creating a new TreeList field that will inherit from TreeList. Or, if you are using MultiList or some other list type, that should work fine as well. Let’s get started.

  1. The first thing you will need to do is create a new class for your custom list field. Ideally you would place this in the Fields folder of your project, if you have one. Edit the class so that it inherits from the appropriate base class, i.e. TreeList.
  2. Then, create a new custom field in Sitecore and publish it. For instructions on how to do that, see my previous blog (link here). In fact, if you already went through the instructions of that post, this is already done.
  3. Going back to your class created in Step 1, you’ll want to override the OnLoad() event and add in the logic to alphabetize. My method was (pseudo code):
    1. Get the current list of guids in the field.
    2. Convert each to an item and add it to a generic list.
    3. Use a generic OrderBy() to sort the list by item display name.
    4. Build a string of pipe-delimited, guids from the sorted list, and set the list field’s value to that.

The result was the following code:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using Sitecore;
using Sitecore.Configuration;
using Sitecore.Data;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
using Sitecore.Resources;
using Sitecore.SecurityModel;
using Sitecore.Shell.Applications.ContentEditor;
using Sitecore.Web.UI.HtmlControls;
using Sitecore.Web.UI.Sheer;
using Sitecore.Web.UI.WebControls;

namespace your.namespace.here
{
    public class AlphaTreeList : TreeList
    {
        protected override void OnLoad(EventArgs args)
        {
            if (!string.IsNullOrEmpty(base.ItemID))
            {
                SortAssociatedProductsAlpha(Sitecore.Context.ContentDatabase.GetItem(base.ItemID));
            }

            base.OnLoad(args);
        }

        private void SortAssociatedProductsAlpha(Item i)
        {
            using (new SecurityDisabler())
            {
                var associatedProducts = i.Fields["Associated Products"];
                var alphaGuids = GetAlphabetizedGuidString(i, associatedProducts);

                if (alphaGuids.Equals(associatedProducts.Value)) return;

                if (alphaGuids != string.Empty)
                {
                    using (new EditContext(i))
                    {
                        this.Value = alphaGuids;
                    }
                }
            }
        }

        private string GetAlphabetizedGuidString(Item i, Field f)
        {
            List<Item> items = new List<Item>();
            StringBuilder scGuidBuilder = new StringBuilder();

            if (i != null && f != null)
            {
                foreach (ID guid in ((MultilistField)f).TargetIDs)
                {
                    Item target = Sitecore.Data.Database.GetDatabase("master").Items.GetItem(guid);
                    items.Add(target);
                }

                // Sort it by item name.
                items = items.OrderBy(o => o.DisplayName, StringComparer.OrdinalIgnoreCase).ToList();

                // Build a string of pipe-delimited guids.
                foreach (Item item in items)
                {
                    scGuidBuilder.Append(item.ID);
                    scGuidBuilder.Append("|");
                }

                // Return string which is a list of guids. And remove that last pipe.
                return scGuidBuilder.ToString().TrimEnd('|');
            }

            return string.Empty;
        }
    }
}

Lastly, you should change the field type for this field in your environment to your new custom field type.

This should work every time you open the item or add a new item to the field’s Selected list and save.