7 July 2014

Custom FIeld


Today I just wanted to show how you can create a custom field. Even if you will not really use this everyday. I found it quite handy from time to time. You can use custom fields for lots of reason: getting data from third party (Youtube, Google Map...) and store it in the field. You can also use it to display your field value in a certain way: preview your video... I guess it all depends on where your imagination can take you.

So on this example, I did not wanted to tackle something too tricky and connecting to third party... So what I was thinking about doing something like having this field to store a list of Json Object that could be used for a 301 redirect... Let say for example that you want to create a 301 redirect system that would look like:


Ok, so obviously when you will create your pipeline action to intercept the HttpRequestBegin to resolve the 301, you could just loop through all the items and find the item relevant to your request. Well, it is totally possible. Also considering a full site rebuild with a large amount of pages you would need to loop through 1000 links (I made up the number...). Anyway for our purpose today, the approach we will take is to define the list of JSon objects that will represent all our redirect items and store it on the parent item in our custom field. In our pipeline action we will then need to get the field value and deserialize the data and find the relevant redirect item. Here is how the field would appear


Well as you can image, this list will not be updated unless a content editor add a new item... That is why in that case a custom field is quite handy... especially with a nice little button at the top to regenerate the data when you will need to.

1- Defining the field type.

 To define the field type, you need to go to the CORE DB in the desktop. Under the System folder, you need to locate the item: Field Type. Create a new folder underneath which you can call Custom Field Types.
Under this newly created folder, you can create an Item from template Template field type. You can see the value to the control field: myfields:JsonField


2- Defining the menu item: button appearing at the top of the field

To define the menu item, you need to create a new folder under the field type you just created. The template to use is /Common/Folder. Under this folder, you will need to create a anew item from the template Menu item. The Display name field will be used for the text that will be displayed on the button. Also note the value placed on the Message field: myfields:regeneratejson.



3- Configurations

As you may have noticed we created some reference to controls: myfields:...
We now need to map it to the code we will write. So create a new config file in the Include folder. In this configuration file, we are going to define the Control to point it to a .net class that we will create.

    < controlsources>
      < source assembly="MySite.Business" mode="on" namespace="MySite.Business.Sitecore.Fields" patch:after="source[@namespace='ComponentArt.Web.UI']" prefix="myfields" />
    < /controlsources>


As you can see the myfields prefix will be mapping to the Namespace MySite.Business.Sitecore.Fields. Now, I am sure you are wondering where is the class defined. Well "myfields:JsonField", your .NET class will need to be called JsonField.

4- our code

OK, now we have the config in place, we can create our class... Namespace is defined above and the class name: JsonField. The field will be quite simple: we just need a free text on which will store Text... If you look into the namespace: Sitecore.Shell.Applications.ContentEditor you will some base control that you can base your control on. In our case we will choose to Sitecore.Shell.Applications.ContentEditor.Text as base type. Our first draft of the code will be similar to

namespace MySite.Business.Sitecore.Fields
{
    public class JsonField : SC.Shell.Applications.ContentEditor.Text
    {
        protected override bool LoadPostData(string value)
        {
            value = SC.StringUtil.GetString(new string[1]
            {
                value
            });

            if (this.Value == value)
                return false;

            this.Value = value;

            return true;
        }
    }
}


you can verify that you field display as a text box and the value you place in the field will be saved correctly.

5- Button action

The button action. Well in the definition of the menu item (button), we defined the Message field: myfields:regeneratejson
If you decompile the code for the base control: Sitecore.Shell.Applications.ContentEditor.Text, you will see that there are a few method you can override. One of them is the HandleMessage. This will allow you to intercept the regeneratejson coming from the button and redirect your code to your own method to hande the action:

        public override void HandleMessage(Message message)
        {
            base.HandleMessage(message);
            switch (message.Name)
            {
                case "myfields:regeneratejson":
                    this.RegenerateData();
                    break;
            }
        }


In our case, you can note that we wanted to loop through all children and serialised the data to build our Json list of redirect item... So first, we need to ensure we can access the actual current Item. This can be done as per the following:

        public string ItemID
        {
            get
            {
                return base.GetViewStateString("ItemID");
            }
            set
            {
                Assert.ArgumentNotNull(value, "value");
                base.SetViewStateString("ItemID", value);
            }
        }
Now we have the item we can build our method to loop through children and build our serialisation. Here is the full code:

namespace MySite.Business.Sitecore.Fields
{
    public class JsonField : SC.Shell.Applications.ContentEditor.Text
    {
        public string ItemID
        {
            get
            {
                return base.GetViewStateString("ItemID");
            }
            set
            {
                Assert.ArgumentNotNull(value, "value");
                base.SetViewStateString("ItemID", value);
            }
        }

        public override void HandleMessage(Message message)
        {
            base.HandleMessage(message);
            switch (message.Name)
            {
                case "myfields:regeneratejson":
                    this.RegenerateData();
                    break;
            }
        }

        protected void RegenerateData()
        {
            var masterDB = SC.Data.Database.GetDatabase("master");
            if (masterDB == null)
            {
                SheerResponse.ShowError("Could not get master DB", "Error: Could not retrieve the Master Database");
            }

            var item = masterDB.GetItem(ItemID);
            if (item == null)
            {
                SheerResponse.ShowError("Could not resolve the current Item", "Error: Could not resolve the current Item");
            }

            if(!item.HasChildren)
                return;

            List list = new List();
            foreach (var child in item.Children)
            {
                RedirectItem rItem = new RedirectItem((Item)child);
                if (rItem.isValid)
                {
                    list.Add(rItem);
                }
            }

            string serialization = Newtonsoft.Json.JsonConvert.SerializeObject(list);

            this.Value = serialization;
        }

        protected override bool LoadPostData(string value)
        {
            value = SC.StringUtil.GetString(new string[1]
            {
                value
            });

            if (this.Value == value)
                return false;

            this.Value = value;

            return true;
        }
    }
}


You can now try to view the field and click on the button:


No comments:

Post a Comment