Sitecore Name Value List field with special characters

The question came up and wrote it to Sitecore StackExchange. Thank you @jammykam for your answer.

The default Sitecore Name Value List field has an annoying issue. It does not allow to use special characters like ‘-‘ for the key. I just digged into the Sitecore.Kernel library and I found a regular expression validation which is hardcoded.

Let’s overwrite this class make it more flexible. The main issue with the implementation that they don’t encode/decode the URI.

With the following class is the implementation of the new NameValueList field which allows you special characters:

using System;
using System.Collections.Specialized;
using System.Linq;
using System.Web;
using System.Web.UI;
using Sitecore;
using Sitecore.Diagnostics;
using Sitecore.Shell.Applications.ContentEditor;
using Sitecore.Text;
namespace Sitecore.Foundation.Fields.ContentEditor.Controls
{
/// <summary>
/// Contains some method which are copied from Sitecore.Kernel.dll 8.1.0 rev. 160519.
/// HACK: check this class implementation whenever do a Sitecore update.
/// </summary>
public class FreeNameValue : NameValue
{
protected override void OnLoad(EventArgs e)
{
Assert.ArgumentNotNull(e, "e");
if (Sitecore.Context.ClientPage.IsEvent)
{
this.LoadValue();
}
else
{
this.BuildControl();
}
}
/// <summary>
/// Copied without invalid character validation.
/// </summary>
protected new void ParameterChange()
{
var clientPage = Sitecore.Context.ClientPage;
if (clientPage.ClientRequest.Source == StringUtil.GetString(clientPage.ServerProperties[this.ID + "_LastParameterID"]) && !string.IsNullOrEmpty(clientPage.ClientRequest.Form[clientPage.ClientRequest.Source]))
{
var emptyRow = this.BuildParameterKeyValue(string.Empty, string.Empty);
clientPage.ClientResponse.Insert(this.ID, "beforeEnd", emptyRow);
}
clientPage.ClientResponse.SetReturnValue(true);
}
/// <summary>
/// It calls the copied method with the name value collection from the request form.
/// </summary>
private void LoadValue()
{
var page = HttpContext.Current.Handler as Page;
this.LoadValue(page == null ? new NameValueCollection() : page.Request.Form);
}
/// <summary>
/// Copied without replacing the '-' with '_' and with URL encode.
/// </summary>
public void LoadValue(NameValueCollection formCollection)
{
if (this.ReadOnly || this.Disabled)
{
return;
}
var collection = formCollection ?? new NameValueCollection();
var urlString = new UrlString();
foreach (string key in collection.Keys)
{
if (!string.IsNullOrEmpty(key) && key.StartsWith(this.ID + "_Param", StringComparison.InvariantCulture) && !key.EndsWith("_value", StringComparison.InvariantCulture))
{
var resultKey = collection[key];
var resultValue = collection[key + "_value"];
if (!string.IsNullOrEmpty(resultKey))
{
urlString[HttpUtility.UrlEncode(resultKey)] = HttpUtility.UrlEncode(resultValue) ?? string.Empty;
}
}
}
this.Value = urlString.ToString();
}
/// <summary>
/// Copied with URL encode.
/// </summary>
public void BuildControl()
{
var urlString = new UrlString(this.Value);
foreach (var key in urlString.Parameters.Keys.Cast<string>().Where(key => key.Length > 0))
{
this.Controls.Add(new LiteralControl(this.BuildParameterKeyValue(HttpUtility.UrlDecode(key), HttpUtility.UrlDecode(urlString.Parameters[key]))));
}
this.Controls.Add(new LiteralControl(this.BuildParameterKeyValue(string.Empty, string.Empty)));
}
/// <summary>
/// Copied without any changes.
/// </summary>
private string BuildParameterKeyValue(string key, string value)
{
Assert.ArgumentNotNull(key, "key");
Assert.ArgumentNotNull(value, "value");
var uniqueId = GetUniqueID(this.ID + "_Param");
Sitecore.Context.ClientPage.ServerProperties[this.ID + "_LastParameterID"] = uniqueId;
var clientEvent = Sitecore.Context.ClientPage.GetClientEvent(this.ID + ".ParameterChange");
var isReadonly = this.ReadOnly ? " readonly=\"readonly\"" : string.Empty;
var isDisabled = this.Disabled ? " disabled=\"disabled\"" : string.Empty;
var isVertical = this.IsVertical ? "</tr><tr>" : string.Empty;
return
string.Format(
"<table width=\"100%\" class='scAdditionalParameters'><tr><td>{0}</td>{2}<td width=\"100%\">{1}</td></tr></table>",
string.Format(
"<input id=\"{0}\" name=\"{1}\" type=\"text\"{2}{3} style=\"{6}\" value=\"{4}\" onchange=\"{5}\"/>",
uniqueId, uniqueId, isReadonly, isDisabled, StringUtil.EscapeQuote(key), clientEvent, this.NameStyle),
this.GetValueHtmlControl(uniqueId, StringUtil.EscapeQuote(HttpUtility.UrlDecode(value))), isVertical);
}
}
}

To use this field you should add it to Sitecore to the following place in core database.

capture

You also need to add the following to the Sitecore config to make this field used by the Sitecore field item.

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/&quot; xmlns:set="http://www.sitecore.net/xmlconfig/set/"&gt;
<sitecore>
<controlSources>
<source mode="on" namespace="Sitecore.Foundation.Fields.ContentEditor.Controls" assembly="Sitecore.Foundation.Fields" prefix="custom" />
</controlSources>
</sitecore>
</configuration>

5 thoughts on “Sitecore Name Value List field with special characters

  1. It’s been a while, but this is still relevant.Thank you.

    One things to note though is that you need to provide the assembly and class when you add the item to the core DB. See https://www.codeproject.com/Tips/1069299/Extending-Sitecore-Field-Types for an example. The assembly name is just that (unlike other places where you need to put the namespace with the assembly name). And the class name is the fully qualified (with the namespace).

    Like

    1. Also, NameValue will replace your special character when the onload() method is called due to the base call (which you’ve removed completely). I didn’t want to remove that call because it does a lot of stuff upstream from Input. The way I got this to work for me was to simply make my new class detached from NameValue (so it just extends Input) and make the necessary tweaks.

      Like

      1. It’s been a while, so I don’t remember completely why I removed the base call for OnLoad. Usually better, to keep it and call the base method. I would have to post the Sitecore version as it is quite hacky way to solve the problem and it can differ on different versions.

        Like

Leave a comment