Category Archives: Blocks

Support for Block as Property in the Episerver Content Delivery API

Since Episerver 7, CMS has support to add Blocks as Properties. This helps you to group Properties easier as well as reusing code.

Unfortunately the Episerver Content Delivery API does not support these blocks. They are simply not serialized into the JSON response.

In my example I will use the StartPage from the Alloy Templates that has the Block SiteLogotypeBlock added as a Property:

public virtual SiteLogotypeBlock SiteLogotype { get; set; }

When I use the Content Delivery API to request the Start Page, I don’t see it in the JSON response.

no logo

The structure of Block as Property

Without digging too deep into how Episerver structure the Blocks when they are added as properties, you can find that the PropertyData for the SiteLogotypeBlock is defined as BlockProperty.

Normally I would simply need to create a PropertyModel that handles BlockProperty and handles the Block property values similar to how I add PropertyModels with complex value models.

But I don’t want to do that for every Block Type that might be added as a Block!

Extending with a PropertyModelConverter

Fortunately, all BlockProperty inherit from BlockProperty!

This makes it easier for me to create my own PropertyModelConverter (the part responsible of finding a PropertyModel for the PropertyData) and check if the current PropertyData is assignable from BlockProperty.

Default:

propertyModel.PropertyType == propertyData.GetType()

Which does not consider heritance.

My implementation:

propertyData is PropertyBlock;

With this, I can take any BlockProperty into my own PropertyModel.

The BlockPropertyModel

What I did was to create a PropertyModel that is using the ContentModelMapper to serialize the Block. This is the same function that is used to make the requested Content into a serializable model.

Unfortunately the ContentModelMapper can only handle IContent. PageData implements IContent and so does Blocks that are created in the Assets Pane due to Episerver’s runtime proxy.

However Blocks that are added as Properties does not implement IContent.

Dirty workaround ;)

So how can I ask the ContentModelMapper to map my non-IContent Block? This is not the best solution I’ve found but I ask the IContentRepository to create a Default instance of the Block Type in the same way as if I would create a block programmatically.

var fakeContent = this.contentRepository.GetDefault(ContentReference.SiteBlockFolder, contentTypeId);

After that I copy all of the property values from the original Block into my fake instance.

var propertyDataCollection = block.Property.CreateWritableClone();
foreach (var propertyData in propertyDataCollection)
{
    fakeContent.Property.Set(propertyData.Name, propertyData);
}

And finally I ask the ContentModelMapper to map this into a ContentModel.

this.contentModelMapper.TransformContent((IContent)fakeContent);

After cleaning upp things that I don’t really need like Name, ParentLink etc. I can use this as the Value of the PropertyModel.

Summary

To be able to accomplish this, I needed to be able to find a way to handle all different types of PropertyBlocks and be able to serialize the actual Block Types.

Using the BlockPropertyModelConverter I am able to map the BlockProperty of any type into my BlockPropertyModel, which in turn use BlockModelMapper to create a serializable model using the normal ContentModelMapper.

Now I will get any Block that is added as a Property serialized as JSON into the Content Delivery API Response!

logo

If you want to understand more about the Episerver Content Delivery API serializing, you can read it in the documentation.

Advertisements

Extend your functionality by using Blocks

I’ve found that you can create a small toolbox for the editor to change the behavior of your functionality by using Blocks, not as models but as pluggable functions. It’s quite similar to visual programming where you play around with boxes and arrows but in this case we create Blocks and drag them either to a ContentReference or a ContentArea.

Functionality Blocks

The idea came from a project where multiple web sites are using their own instance of the same EPiServer project. Each website had different requirements of filters and I needed something generic where a super user could setup and change the functionality.

Cut to the code

First I created an interface that I will put on each BlockType:

    public interface IDoSomethingWithPageLists : IContentData
    {
        IEnumerable<PageData> DoSomething(IEnumerable<PageData> pages);
    }

In my first implementation, say you’re listing lots of pages and the editor wants to reverse the order, this is a really silly example though.

    [ContentType(DisplayName = "Reverse pages", GUID = "1f58e116-6576-4737-8d2c-246010b5b302")]
    public class ReverseBlock : BlockData, IDoSomethingWithPageLists
    {
        [Display(
            Name = "Property Name",
            GroupName = SystemTabNames.Content,
            Order = 1)]
        public virtual string PropertyName { get; set; }

        public IEnumerable<PageData> DoSomething(IEnumerable<PageData> pages)
        {
            return pages.Reverse();
        }
    }

That was quite easy, you can also give the editor the possibility to give details or variables to the logic. Such as filtering on that a property requires a value.

    [ContentType(DisplayName = "Property must contain value", GUID = "25ed56de-0d0b-43bb-8beb-8a8442e1bdd1")]
    public class PropertyMustContainValueBlock : BlockData, IDoSomethingWithPageLists
    {
        [Display(
            Name = "Property Name",
            GroupName = SystemTabNames.Content,
            Order = 1)]
        public virtual string PropertyName { get; set; }

        public IEnumerable<PageData> DoSomething(IEnumerable<PageData> pages)
        {
            return pages.Where(p => p[this.PropertyName] != null);
        }
    }

And just for the case of repeating the possibility of letting the editor decide.

    [ContentType(DisplayName = "Take every #", GUID = "7b9f13a6-22fe-4c0f-a804-ff81879fa986")]
    public class TakeEveryBlock : BlockData, IDoSomethingWithPageLists
    {
        [Display(
            Name = "Take Every",
            GroupName = SystemTabNames.Content,
            Order = 1)]
        public virtual int TakeEvery { get; set; }

        public IEnumerable<PageData> DoSomething(IEnumerable<PageData> pages)
        {
            for (int i = 0; i < pages.Count(); i++)
            {
                if (i % this.TakeEvery == 0)
                {
                    yield return pages.ElementAt(i);
                }
            }
        }
    }

So in my Page Template, all I need to do iterate through my Blocks and let them do what they’re good at.

    IEnumerable<PageData> pages = GetChildren(CurrentPage.PageLink); // Getting children is only a simple example.

    if (CurrentPage.FunctionBlocks != null)
    {
        var blocks = this.CurrentPage.FunctionBlocks.FilteredItems.Select(x => this.Get(x.ContentLink)).Where(x => x != null);
        foreach (var block in blocks)
        {
            pages = block.DoSomething(pages);
        }
    }

    // Do something with the pages such as binding a Repeater/PageList or adding to a Model

Summary

This is a really simplified example how the editor easily plugg in some customized logic to my existing function.

It can sometimes be a little confusing for the editor if you use Functionality Blocks to create a too complex solution. But to simplify for the editor, you can use EditorDescriptors or creating custom Dojo components to help your Editor.

In this example, I’m automatically adding the filters. You can also render these blocks to the visitor and let the visitor activate the logic.

Creating Modular Settings with Blocks

2014-05-16: Updated the code to include code that disappeared due to non html-encoded code.

Through the years there has been many ways to add settings to an EPiServer site. To mention a few there are GuiPlugins in the Admin Mode Plug-in Manager, Dynamic Properties, Global Properties on the Start Page, special Settings Page Types etc.

First place: The Settings Page!

SettingsPage One of my favourites was the concept of a “Settings Page”! You’ve got version history, language management, you’re not mixing content and settings! But I would like to have it a little more modular where I could contain the Settings data close to where it is used. For example if I had a Gallery I would like to keep the Gallery settings in that Namespace or even that assembly. But after only a year in a busy project, that Settings Page Type could be bloated with properties, tabs and what not! That I had to add more Properties to that greedy all-knowing SettingsPage class each time I need to add a few more settings made that Page Type to something big and ugly that only a mother would love. Not to mention how difficult it is to find that exact property in Edit Mode and reversing a version in the version manager without accidentally reversing some other setting for some other module.

The runner up: Plug-ins!

GuiPlugIn This is an old sturdy trustworthy concept. I can even drop a complete assembly containing almost everything I need to have my Image Gallery (just as an example) up and running and for settings can easily be managed in the Plug-in Manager without being mixed with other settings in the code structure. But with today’s features where we’re getting used to version and language management we are no longer interested in this old plain data management. Not to forget that the user has to be an administrator to be able to access the settings and we’re easily getting back to the bloated editor view filled with many fields screaming for attention.

The new kid in town: Blocks!

SettingsBlock Now with Blocks in EPiServer 7 the concept “A Page in EPiServer is necessarily not something you can browse to” becomes a fact since a PageData in previous versions was used to contain data of various sorts – such as Settings. By using Blocks in EPiServer for settings, not only can the settings be modular, but you also have some sweet toys such as

  • Access Rights management through the ACL (and the forget-me-not Access to Tabs). If you play your cards right you could also set different settings for different users based on VisitorGroups and Roles.
  • Version management that only handles versions for your current module.
  • Language Management and even being able to set Fallback Languages for a specific Settings.
  • Fallback/Default values capabilities where you can add logic to the Get/Set Properties as well as the SetDefaultValues method.

I’m aware of the cons where it can end up with multiple Blocks instead of a single view filled with multiple fields. I think that this is a start where we can add a rich Settings Manager and improve the experience for the Editor and still have a strong and flexible data layer underneath. I have made a quick Proof of Concept of my idea and it can be found on GitHub.

Cut to the Code!

First, I created an Interface, in this Draft it doesn’t do anything else except helping me identify that this is one of my Settings.

public interface ISettingsBlock : IContentData
{
}

All I need to do is to implement this to my Block. Note that I’ve also made this unavailable for editors. I don’t want this thing being created all over the place!

[ContentType(DisplayName = "My Settings", GUID = "bc9d37bd-c725-4b8b-8a23-039710f476bf", Description = "", AvailableInEditMode = false)]
public class MySettingsBlock : BlockData, ISettingsBlock
{
    [Display(
        Name = "My Setting",
        GroupName = SystemTabNames.Content,
        Order = 1)]
    public virtual string MySetting { get; set; }
        
    [Display(
        Name = "My Setting with a Fallback",
        GroupName = SystemTabNames.Content,
        Order = 2)]
    public virtual string MySettingWithFallback {
        get
        {
            var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
            return this["MySettingWithFallback"] as string
                ?? contentLoader.Get(ContentReference.StartPage).Name;
        }
        set { this["MySettingWithFallback"] = value; } }
}

And since we want to easily find our Settings Blocks and don’t want to manually create them, I’ve added a little utility class for this.

public class SettingsRepository<T> where T : BlockData, ISettingsBlock
{
    #region [ Private Fields ]
    private const string RootFolderName = "[Settings]";
    private static readonly ConcurrentDictionary<string, object> MiniLocks = new ConcurrentDictionary<string, object>();

    #endregion

    protected virtual ContentReference ParentFolder
    {
        get
        {
            return GetOrCreate(ContentReference.SiteBlockFolder, "[Settings]");
        }
    }

    protected virtual string DefaultBlockName
    {
        get
        {
            var repo = ServiceLocator.Current.GetInstance<BlockTypeRepository>();
            var blockType = repo.Load<T>();
            return blockType.DisplayName;
        }
    }

    public T Instance
    {
        get
        {
            return GetOrCreateSettingsBlock();
        }
    }

    protected virtual T GetOrCreateSettingsBlock()
    {
        ContentReference contentLink = this.GetOrCreate(this.GetOrCreateContentFolder(), this.DefaultBlockName);
        var repo = ServiceLocator.Current.GetInstance<IContentRepository>();
        return repo.Get<T>(contentLink);
    }

    private ContentReference GetOrCreateContentFolder()
    {
        var repo = ServiceLocator.Current.GetInstance<IContentRepository>();
        ContentReference contentLink;
        var child = repo.GetChildren<ContentFolder>(ContentReference.SiteBlockFolder, LanguageSelector.AutoDetect(true)).FirstOrDefault(x => x.Name.Equals(RootFolderName));

        if (child == null)
        {
        const string Key = "ContentFolder";

        // Lock to prevent duplicates from being created.
        object miniLock = MiniLocks.GetOrAdd(Key, k => new object());

        lock (miniLock)
        {
            var childInLock = repo.GetChildren<ContentFolder>(ContentReference.SiteBlockFolder, LanguageSelector.AutoDetect(true)).FirstOrDefault(x => x.Name.Equals(RootFolderName));

            if (childInLock == null)
            {
            var clone = repo.GetDefault<ContentFolder>(ContentReference.SiteBlockFolder);
            clone.Name = RootFolderName;
            contentLink = repo.Save(clone, SaveAction.Publish, AccessLevel.NoAccess);
            }
            else
            {
            contentLink = childInLock.ContentLink;
            }

            // Saving some space
            object temp1;

            if (MiniLocks.TryGetValue(Key, out temp1) && (temp1 == miniLock))
            {
            object temp2;
            MiniLocks.TryRemove(Key, out temp2);
            }
        }
        }
        else
        {
        contentLink = child.ContentLink;
        }

        return contentLink;
    }

    private ContentReference GetOrCreate(ContentReference parentLink, string name)
    {
        var repo = ServiceLocator.Current.GetInstance<IContentRepository>();
        ContentReference contentLink;
        var child = repo.GetChildren<T>(parentLink, LanguageSelector.AutoDetect(true)).FirstOrDefault() as IContent;

        if (child == null)
        {
        string key = typeof(T).ToString();
        
        // Lock to prevent duplicates from being created.
        object miniLock = MiniLocks.GetOrAdd(key, k => new object());

        lock (miniLock)
        {
            var childInLock = repo.GetChildren<T>(parentLink, LanguageSelector.AutoDetect(true)).FirstOrDefault() as IContent;

            if (childInLock == null)
            {
            var clone = repo.GetDefault<T>(parentLink) as IContent;
            clone.Name = name;
            contentLink = repo.Save(clone, SaveAction.Publish, AccessLevel.NoAccess);
            }
            else
            {
            contentLink = childInLock.ContentLink;
            }

            // Saving some space
            object temp1;

            if (MiniLocks.TryGetValue(key, out temp1) && (temp1 == miniLock))
            {
            object temp2;
            MiniLocks.TryRemove(key, out temp2);
            }
        }
        }
        else
        {
        contentLink = child.ContentLink;
        }

        return contentLink;
    }
}

To get the setting I’m interested in, all I need to do is

var mySetting = new SettingsRepository<MySettingsBlock>().Instance.MySetting;

My next step would be to maybe move some of the information such as the Block Name to the Interface (and by that to the Block class itself) instead of the Repository but that feels more like a religious discussion. But the thing that would really improve this concept would be a some kind of interface to get an overview of (and possibly edit) all Settings.

The quest of FindPagesWithCriteria with a Block Property in EPiServer 7 CMS

There are several exciting things with the new Blocks in EPiServer 7 CMS. One of the most convenient things when you need to add a group of data to a page type, for example an image + alternative text or an url + clickable text or maybe a group of SEO data such as title, description and keywords.

Note that this article does not include how Global Blocks are saved. I have only focused on Blocks that are added as Properties on a Content Type.

I have create a sample Page Type TestPage containing 2 sets of the block TestBlock which itself only contains the string Property TestValue. TestBlock1 contains the value “abc” and TestBlock2 contains the value “def”.

When I check the database structure I can find these traces of ContentTypes, Properties and values. I have marked the recurring values to mark how the relations are set between all  tables.

Database Structure

[tblContentType]

pkID ContentTypeGUID ModelType Name ContentType
23 34CC512A-5680-47CA-86EF-80F38A688F40 EPiServer.Templates.Alloy.Models.Blocks.TestBlock, EPiServer.Templates.Alloy, Version=1.0.0.30998, Culture=neutral, PublicKeyToken=null TestBlock 1
24 64E21501-096A-4983-AE87-03D274FE98B2 EPiServer.Templates.Alloy.Models.Pages.TestPage, EPiServer.Templates.Alloy, Version=1.0.0.30998, Culture=neutral, PublicKeyToken=null TestPage 0

[tblPropertyDefinitionType]

pkID Property Name TypeName AssemblyName fkContentTypeGUID
1010 12 TestBlock NULL NULL 34CC512A-5680-47CA-86EF-80F38A688F40

Where the column fkContentTypeGUID is null on PropertyDefinitionTypes that are not connected to a ContentType

[tblPropertyDefinition]

pkID fkContentTypeID fkPropertyDefinitionTypeID Name Property
133 23 7 TestValue 7
134 24 1010 TestBlock1 12
135 24 1010 TestBlock2 12

Where fkPropertyDefinitionTypeID 7 mean it’s a normal string and strangely 12 is a XhtmlString but I guess it has something to do with rendering. *adding to future blogs list*

[tblContentProperty]

pkID fkPropertyDefinitionID fkContentID fkLanguageBranchID ScopeName guid LongString LongStringLength
1628 133 183 1 .134.133. 1C2D6A8D-6973-4E36-9BC9-9E655506795C abc 6
1628 133 183 1 .135.133. 1C2D6A8D-6973-4E36-9BC9-9E655506795C def 6

I have created a Page of TestPage. The page have Id 183, it didn’t feel necessary to add that table to this chart but as you can see, the values of TestBlock1 and TestBlock2 have a fkContentID that referers to the PageData it is stored on.

The column ScopeName is a relational path that connects the Name property to the TestBlock Block named TestBlock1.

Find Pages based on this?

The datastructure for Blocks in tblContrentProperty are quite flat. Therefore I can find my Test Page by creating a FindPagesWithCriteria:

IPageCriteriaQueryService repository = ServiceLocator.Current.GetInstance();

PropertyCriteriaCollection criterias = new PropertyCriteriaCollection
{
    new PropertyCriteria
    {
        Condition = CompareCondition.Equal,
        Required = true,
        Name = "TestValue",
        Type = PropertyDataType.String,
        Value = "abc"
    }
};
PageDataCollection result = repository.FindPagesWithCriteria(ContentReference.RootPage, criterias);

FindPagesWithCriteria sends queries to a couple of Stored Procedures in the database and checks among the tables whether the Pages have a Property with the given name and  if the Property value fits the criteria.

And what is the result?

Unfortunately since there is no way to define that I only want to search for a page where TestBlock1 have a TestValue with value “abc”, I will receive ALL pages that have a Property called TestValue with a value of “abc” no matter if they are saved on a block or directly on the page.

This really sounds like a job for EPiServer Find but if you don’t have it installed, FindPagesWithCriteria can be quite good at times except for the disadvantages of many queries to the database and its cumbersome API.

Though the advantages is its stability and being straightforward. Remember to not add too many criterion and try to Cache-smart if you using it

What I can find there is no good way to change the behaviour of FindPagesWithCriteria without copying quite alot of the existing logic or rewriting the existing Stores Procedures.