Migrate page attributes to custom fields

IMPORTANT: Custom pages attributes is no longer the recommended approach to add dynamic fields to pages. Sitefinity CMS since version 7.0 provides the Custom fields feature, which you should use instead.

Custom fields differ from page attributes in that they provide more extensibility in storing different types of data. They are easier to use for developers, because they mimic static properties better.

Use the following code sample to migrate attributes to custom fields:

PREREQUISITES: Before the migration you must create custom field for Pages. Follow the procedure Create custom fields. After you create the custom field, to set the attribute value, use pageNode.SetValue("{CustomFieldName}", value) where the CustomFieldName corresponds to the name of your custom field.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Telerik.Sitefinity.Abstractions;
using Telerik.Sitefinity.Modules.Pages;
using Telerik.Sitefinity.Model;
using ServiceStack.Text;

namespace Telerik.Sitefinity.Documentation.CodeSnippets.DevGuide.SitefinityEssentials.Pages.CustomFields
{
    public partial class PagesCustomFieldsSnippets
    {
        public void MigrateAttributes()
        {
            var manager = PageManager.GetManager();
            var pageIds = manager.GetPageNodes().Where(x => x.RootNodeId != SiteInitializer.BackendRootNodeId).Select(x => x.Id).ToList();
            int commitCounter = 0;
            foreach (var id in pageIds)
            {
                var pageNode = manager.GetPageNode(id);
                if (pageNode.Attributes.Count > 0)
                {
                    foreach (var attr in pageNode.Attributes)
                    {
                        var type = GetEntryType(attr.Key, attr.Value);
                        if (type == typeof(Boolean))
                        {
                            var value = Boolean.Parse(attr.Value);
                            pageNode.SetValue("MigratedBool", value);
                        }
                        else if (type == typeof(Int32))
                        {
                            var value = Int32.Parse(attr.Value);
                            pageNode.SetValue("MigratedInt", value);
                        }
                        else if (type == typeof(Object[]))
                        {
                            var value = JsonSerializer.DeserializeFromString<Object[]>(attr.Value);
                            //here you should see if this property is a choice field or a guid array
                            //and appropriately handle the parsing of the objects
                            //set the guids
                            pageNode.Organizer.AddTaxa("Tags", value.Select(x => Guid.Parse(x.ToString())).ToArray());
                            //or set the choices
                            //pageNode.SetValue("MigratedChoices", value.Select(x => x.ToString()).ToArray());
                        }
                        else if (type == typeof(Lstring))
                        {
                            var value = attr.Value;
                            pageNode.SetString("MigratedString", value);
                        }
                    }
                    commitCounter++;
                }
                if (commitCounter == 10)
                {
                    manager.SaveChanges();
                    commitCounter = 0;
                }
                manager.SaveChanges();
            }
        }
        private Type GetEntryType(string key, string value)
        {
            if (!string.IsNullOrEmpty(value) && value.StartsWith("[") && value.EndsWith("]"))
                return typeof(Object[]);
            string typeSufix = null;
            var underscoreLocation = key.LastIndexOf('_');
            if (underscoreLocation > 0 && underscoreLocation < (key.Length - 2))
            {
                typeSufix = key.Substring(underscoreLocation + 1);
            }
            if (!string.IsNullOrEmpty(typeSufix))
            {
                switch (typeSufix.ToLowerInvariant())
                {
                    case "boolean":
                        return typeof(Boolean);
                    case "integer":
                        return typeof(Int32);
                }
            }
            return typeof(Lstring);
        }
    }
}

NOTE: After the migration is completed you must change the configuration of the FieldControls in the backend. In the DataFieldName property, replace Attributes with CustomFields. For example, replace Attributes.SampleText with CustomFields.SampleText.

Want to learn more?
Enhance your Sitefinity skills by enrolling in free training sessions. Become Sitefinity certified through Progress Education Community to strengthen your professional credentials.
New to Sitefinity?