Retrieving Available Content Types in EPiServer

An average EPiServer solution contains many different content types. Using Microsoft’s default conventions makes it difficult to maintain and further develop over time. If we are lucky enough to have an information architecture ready before we start writing code, we can use the Feature Folders architecture to organize the files in our solution.

However, after a few development iterations (sprints, if you’re using Scrum), it can be difficult to keep an overview of all content types and their properties. We can always look in Visual Studio or click through different menus in edit and admin mode, but that’s just too time-consuming. It would be nice to have all that information in one place, in a user-friendly format, and be able to filter and export the data to a PDF file.

For that reason, I’ve created an API that returns available content types and their properties as a JSON object. This can be used for building plugins and reports that displays that information in a user-friendly format.

Code

episerver - file structure

public class AvailableContentTypesController : Controller
{
    private readonly IContentTypeRepository _contentTypeRepository;
    private readonly IAdministrationSettingsService _administrationSettingsService;
    private readonly ContentTypeAvailabilityService _contentTypeAvailabilityService;
    private readonly ModuleTable _moduleTable;
    private readonly ServiceAccessor<SiteDefinition> _currentSiteDefinition;
    private readonly ContentTypeModelRepository _contentTypeModelRepository;

    public AvailableContentTypesController(
        IContentTypeRepository contentTypeRepository,
        IAdministrationSettingsService administrationSettingsService,
        ContentTypeAvailabilityService contentTypeAvailabilityService,
        ModuleTable moduleTable,
        ServiceAccessor<SiteDefinition> currentSiteDefinition,
        ContentTypeModelRepository contentTypeModelRepository)
    {
        _contentTypeRepository = contentTypeRepository;
        _administrationSettingsService = administrationSettingsService;
        _contentTypeAvailabilityService = contentTypeAvailabilityService;
        _moduleTable = moduleTable;
        _currentSiteDefinition = currentSiteDefinition;
        _contentTypeModelRepository = contentTypeModelRepository;
    }

    public JsonResult Index()
    {
        var contentTypes = _contentTypeRepository.List().ToList();

        var pages = new List<PageInfo>();
        var blocks = new List<BlockInfo>();

        foreach (var type in contentTypes)
        {
            var attribute = _administrationSettingsService.GetAttribute(type);

            if (!attribute.Visible || !type.IsAvailable)
            {
                continue;
            }

            if (type is PageType)
            {
                var pageInfo = GetPageInfo(type);
                pages.Add(pageInfo);
            }

            else if (type is BlockType)
            {
                var blockInfo = GetBlockInfo(type);
                blocks.Add(blockInfo);
            }
        }

        var result = new
        {
            Pages = pages,
            Blocks = blocks
        };

        return Json(result, JsonRequestBehavior.AllowGet);
    }

    private BlockInfo GetBlockInfo(ContentType type)
    {
        var blockInfo = new BlockInfo
        {
            Guid = type.ModelType.GUID,
            ModelName = type.ModelType.Name,
            Name = type.LocalizedName,
            Description = type.LocalizedDescription,
            GroupName = type.LocalizedGroupName,
            ImagePath = GetImagePath(type),
            Properties = GetPropertyInfo(type.PropertyDefinitions)
        };

        return blockInfo;
    }

    private PageInfo GetPageInfo(ContentType type)
    {
        var pageInfo = new PageInfo
        {
            Guid = type.ModelType.GUID,
            ModelName = type.ModelType.Name,
            Name = type.LocalizedName,
            Description = type.LocalizedDescription,
            GroupName = type.LocalizedGroupName,
            ImagePath = GetImagePath(type),
            Properties = GetPropertyInfo(type.PropertyDefinitions)
        };

        var availablePageTypes = _contentTypeAvailabilityService
            .ListAvailable(type.Name, PrincipalInfo.CurrentPrincipal)
            .Where(x => x is PageType)
            .ToList();

        if (availablePageTypes.Count > 0)
        {
            pageInfo.AvailablePageTypes = availablePageTypes.Select(x => new AvailablePageInfo
            {
                Guid = x.ModelType.GUID,
                ModelName = x.ModelType.Name,
                Name = x.LocalizedName
            }).ToList();
        }

        return pageInfo;
    }

    private List<PropertyInfo> GetPropertyInfo(PropertyDefinitionCollection propertyDefinitions)
    {
        var properties = propertyDefinitions
            .Where(x => x.TranslateDisplayName() != null)
            .Select(property => new PropertyInfo
            {
                Name = property.TranslateDisplayName(),
                Description = property.TranslateDescription(),
                IsRequired = property.Required,
                IsSearchable = property.Searchable,
                IsLanguageSecific = property.LanguageSpecific,
                TabName = property.Tab.LocalizedName,
                TabSortIndex = property.Tab.SortIndex,
                Type = property.Type.Name,
                SortIndex = property.FieldOrder
            }).ToList();

        return properties.Count == 0 ? null : properties;
    }

    private string GetImagePath(ContentType type)
    {
        var contentTypeModel = _contentTypeModelRepository.GetContentTypeModel(type.ModelType);

        if (contentTypeModel == null)
        {
            return null;
        }

        ImageUrlAttribute imageUrlAttribute;
        if (!contentTypeModel.Attributes.TryGetSingleAttribute(out imageUrlAttribute))
        {
            return null;
        }

        if (string.IsNullOrWhiteSpace(imageUrlAttribute.Path))
        {
            return null;
        }

        string imagePath = null;

        ShellModule shellModule;
        if (!VirtualPathUtility.IsAppRelative(imageUrlAttribute.Path) &&
            !VirtualPathUtility.IsAbsolute(imageUrlAttribute.Path) &&
            _moduleTable.TryGetModule(type.ModelType.Assembly, out shellModule))
        {
            imagePath = ModuleTable.ResolvePath(shellModule, imageUrlAttribute.Path);
        }
        if (string.IsNullOrEmpty(imagePath))
        {
            imagePath = UriSupport.AbsolutePathForSite(imageUrlAttribute.Path, _currentSiteDefinition());
        }

        return imagePath;
    }
}
public class AvailablePageInfo
{
    public Guid Guid { get; set; }
    public string ModelName { get; set; }
    public string Name { get; set; }
}
public class BlockInfo
{
    public Guid Guid { get; set; }
    public string ModelName { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string GroupName { get; set; }
    public string ImagePath { get; set; }

    public List<PropertyInfo> Properties { get; set; }
}
public class PageInfo
{
    public Guid Guid { get; set; }
    public string ModelName { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string GroupName { get; set; }
    public string ImagePath { get; set; }

    public List<PropertyInfo> Properties { get; set; }
    public List<AvailablePageInfo> AvailablePageTypes { get; set; }
}
public class PropertyInfo
{
    public string Name { get; set; }
    public string Description { get; set; }
    public bool IsRequired { get; set; }
    public bool IsSearchable { get; set; }
    public bool IsLanguageSecific { get; set; }
    public string TabName { get; set; }
    public int TabSortIndex { get; set; }
    public string Type { get; set; }
    public int SortIndex { get; set; }
}
[InitializableModule]
public class CustomRoutes : IInitializableModule
{
    public void Initialize(InitializationEngine context)
    {
        RouteTable.Routes.MapRoute(
            null,
            "dc/availablecontenttypes",
            new { controller = "AvailableContentTypes", action = "Index" },
            new[] { "DC.AvailableContentTypes" });
    }

    public void Uninitialize(InitializationEngine context)
    {
    }
}

 Sample output

{
    "Pages": [
        {
            "Guid": "075f58db-1106-3e85-9e07-e50cd9b80a0b",
            "ModelName": "StartPage",
            "Name": "Start Page",
            "Description": "The home page of the website",
            "GroupName": "Specialized",
            "ImagePath": "/Static/gfx/page-type-thumbnail.png",
            "Properties": [
                {
                    "Name": "Page description",
                    "Description": "Used as the meta description and commonly as an ingress",
                    "IsRequired": false,
                    "IsSearchable": true,
                    "IsLanguageSecific": true,
                    "TabName": "Metadata",
                    "TabSortIndex": 3,
                    "Type": "LongString",
                    "SortIndex": 300
                },
                ...
            ],
            "AvailablePageTypes": [
                {
                    "Guid": "69f56878-3c02-37ba-b608-d78b2a7809c8",
                    "ModelName": "StandardPage",
                    "Name": "Standard Page"
                },
                ...
            ]
        },
        ...
    ],
    "Blocks": [
        {
            "Guid": "613532da-e14c-3bb8-b1ae-bd0a60619137",
            "ModelName": "TeaserBlock",
            "Name": "Teaser",
            "Description": "Used to insert a content teaser",
            "GroupName": "Default",
            "ImagePath": "/Static/gfx/page-type-thumbnail.png",
            "Properties": [
                {
                    "Name": "Heading",
                    "Description": null,
                    "IsRequired": true,
                    "IsSearchable": true,
                    "IsLanguageSecific": true,
                    "TabName": "Content",
                    "TabSortIndex": 10,
                    "Type": "LongString",
                    "SortIndex": 1
                },
                ...
            ]
        },
        ...           
        
    ]
}

As you can see, this API combines information from four different screens in EPiServer:

Available Content Types in Edit Mode

Episerver - available content types

 Content Types in Admin Mode

Episerver - content types - admin mode

Property List in Admin Mode

Episerver - property list

Available Page Types in Admin Mode

Episerver - available page types - admin mode

The API doesn’t show Media Types, System Types, Local Blocks, and properties that are missing in the code.

comments powered by Disqus