Setting or Updating Rendering Parameters Programatically in Sitecore

Imagine this scenario, you add a new required rendering parameter for a common sublayout/rendering such as a Header or Footer in an existing Sitecore project. There could be dozens of page templates that use this control, how do you update the rendering parameters of all of these templates quickly and without missing any? I had just run into the this issue and I wanted to share with you the solution to this problem. The extension methods that you see in the code below will be shared at the end of this post.

[csharp] public void FindAndUpdateRenderingParams(
string databaseName,
string templateStartPath,
string presCompId,
string contentStartPath,
string renderingParamKey,
string renderingParamValue,
string deviceId,
bool standardValuesOnly)
{
var database = Database.GetDatabase(databaseName); <li>
var templateRoot = templateStartPath.GetItemByPath(database);

var itemsToUpdate = new List<Item>();

var templates =
templateRoot.Axes.GetDescendants()
.Where(
x => x.Name == "__Standard Values" && x.Fields[FieldIDs.LayoutField].Value.Contains(presCompId));

itemsToUpdate.AddRange(templates);

if (!standardValuesOnly)
{
var contentRoot = contentStartPath.GetItemByPath(database);

var content =
contentRoot.Axes.GetDescendants()
.Where(x => x.Fields[FieldIDs.LayoutField].Value.Contains(presCompId));

itemsToUpdate.AddRange(content);
}

itemsToUpdate.SetRenderingParameterValue(presCompId, renderingParamKey, renderingParamValue, deviceId);
}[/csharp]

The parameters:

  • databaseName – the database in which to make the changes, usually “master”
  • templateStartPath – where the templates are, usually “/sitecore/templates”
  • presCompId – the ID of the rendering (presentation component) that you want to modify, string representation of GUID with {}
  • contentStartPath – the ID of the rendering (presentation component), usually “/sitecore/content”
  • renderingParamKey – key of the rendering parameter to set
  • renderingParamValue – value of the rendering parameter to set
  • deviceId – the id of the device under which the rendering lives, string representation of GUID with {}. When the same rendering lives under two devices then the operation needs to be run separately for each one.
  • standardValuesOnly – indicated whether content items’ presentation will be altered

You only really need to update the Standard Values of templates, you can however include Content items as well if you need to. Using Axes makes this operation much faster, as it directly queries the Sitecore database. Once you have a list of items you can send them into the SetRenderingParameterValue method.

[csharp] public static void SetRenderingParameterValue(
this IEnumerable<Item> itemsToUpdate,
string presCompId,
string paramKey,
string paramValue,
string deviceId = null)
{
if (!string.IsNullOrWhiteSpace(deviceId)
{
deviceId = Context.Device.ID.ToString();
}

using (new SecurityDisabler())
{
foreach (var item in itemsToUpdate)
{
var layoutField = new LayoutField(item.Fields[FieldIDs.LayoutField]);
var layoutDefinition = LayoutDefinition.Parse(layoutField.Value);
var deviceDefinition = layoutDefinition.GetDevice(deviceId);

foreach (RenderingDefinition rendering in deviceDefinition.Renderings)
{
if (rendering != null && rendering.ItemID == presCompId)
{
var parameters = rendering.Parameters.ParseAsQueryString();
parameters[paramKey] = paramValue;
rendering.Parameters = parameters.ToQueryString();
break;
}
}

item.Editing.BeginEdit();
item[FieldIDs.LayoutField] = layoutDefinition.ToXml();
item.Editing.EndEdit();
}
}
}[/csharp]

The parameters are stored as query string, so we parse them into a NameValueCollection using ParseAsQueryString. This way we preserve any previous values and modify only the one that we need. Once we are done with the update we convert the NameValueCollection back to a query string and assign it to the parameters. Finally we set our modified layoutDefinition to the item’s LayoutField after converting to XML.

If you are wondering why I did not use Linq’s Where() extension on the deviceDefinition.Renderings, it is because that is an ArrayList and Where() is not directly supported on that type.

The Extensions Methods that you’ll need are:

[csharp] public static Item GetItemByPath(this string contentPath, Database database = null, Language language = null)
{
if (!string.IsNullOrWhiteSpace(contentPath))
{
database = database ?? Context.Database;

if (database != null)
{
if (language == null)
{
return database.GetItem(contentPath);
}

return database.GetItem(contentPath, language);
}
}

return null;
}[/csharp]

[csharp] public static NameValueCollection ParseAsQueryString(this string query)
{
if (!string.IsNullOrWhiteSpace(query))
{
return HttpUtility.ParseQueryString(query);
}

return new NameValueCollection(StringComparer.InvariantCultureIgnoreCase);
}
[/csharp]

[csharp] public static string ToQueryString(this NameValueCollection collection, bool prependQuestionMark = false)
{
var queryString = new StringBuilder(64);
if (collection != null)
{
var count = collection.Count;
for (var i = 0; i < count; i++)
{
if (prependQuestionMark && i == 0)
{
queryString.Append(‘?’);
}

var key = collection.GetKey(i);
var value = collection[key];

if (!string.IsNullOrEmpty(value))
{
queryString.UrlEncode(key).Append(‘=’).UrlEncode(value);
}

if (i != count – 1)
{
queryString.Append(‘&’);
}
}
}

return queryString.ToString();
}[/csharp]

[csharp] public static StringBuilder UrlEncode(this StringBuilder builder, string input)
{
if (!string.IsNullOrWhiteSpace(input))
{
foreach (var ch in input)
{
if ((((ch > ‘`’) && (ch < ‘{‘)) || ((ch > ‘@’) && (ch < ‘[‘)))
|| (((ch > ‘/’) && (ch < ‘:’)) || (((ch == ‘.’) || (ch == ‘-‘)) || (ch == ‘_’))))
{
builder.Append(ch);
}
else if (ch > ‘\x007f’)
{
builder.Append("%u" + TwoByteHex(ch));
}
else
{
builder.Append("%" + SingleByteHex(ch));
}
}
}

return builder;
}[/csharp]

[csharp]public static string SingleByteHex(char c)
{
uint number = c;
return number.ToString("x").PadLeft(2, ‘0’);
}[/csharp]

[csharp]public static string TwoByteHex(char c)
{
uint number = c;
return number.ToString("x").PadLeft(4, ‘0’);
}[/csharp]

One thought on “Setting or Updating Rendering Parameters Programatically in Sitecore

  1. Ivan Krumov says:

    Hello,

    Thanks for the great article?
    One question – can you please upload the source code of SingleByteHex and TwoByteHex?

    Ivan

Leave a Reply

Your email address will not be published. Required fields are marked *