Category Archives: ContentArea

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.

Find Pages containing a certain Content in a ContentArea (UPDATE)

good-news-everyone

I had some discussions about this blog post and had to check up some things. Accidently I found the IContentRepository.GetReferenceInformationForContent(ContentReference contentLink, bool includeDecendents) (implemented on DataFactory) which runs the Stored Procedure [editDeletePageCheck]. This is normally used to warn an editor about referencing pages when editing och deleting content.

According to the ExpressProfiler it goes to the database each time I run the command so it might be good to keep this in mind.

Here is the new code:

var contents = new List<ContentData>();
var repository = ServiceLocator.Current.GetInstance<IContentRepository>();
IEnumerable references = repository.GetReferencesToContent(contentLink, false);
foreach (ReferenceInformation reference in references)
{
    ContentReference ownerContentLink = reference.OwnerID;
    CultureInfo ownerLanguage = reference.OwnerLanguage;
    ILanguageSelector selector = new LanguageSelector(ownerLanguage.Name);
    var content = repository.Get<ContentData>(ownerContentLink, selector);

    var contentArea = content["ResponsibleEditors"] as ContentArea;
    if (contentArea != null)
    {
        if (contentArea.ContentFragments.Any(fragment => fragment.ContentLink == contentLink))
            contents.Add(content);
    }
}

While browsing in the tblContentProperty I stumbled upon how a ContentArea is saved in the database.

It’s quite similar to how Dynamic Content is serialized in a XhtmlString:

<div data-classid=”36f4349b-8093-492b-b616-05d8964e4c89″ data-contentguid=”7cef1c69-b345-493c-a60e-ce011fe42c31″ data-contentlink=”35″ data-contentname=”Content Name”>{}</div><div data-classid=”36f4349b-8093-492b-b616-05d8964e4c89″ data-contentguid=”216210e0-f687-4f7a-9bc9-c2a51badab1a” data-contentlink=”701″ data-contentname=”Another Content Name”>{}</div>

In my example I have a Page Type called EditorPage presenting all persons in the staff who can be responsible to the web sites articles.

To present an articles responsible editor I have added a ContentArea where the editor simply drops Editor Pages.

On the Editor Page I want to list all the article where the Editor is connected.

So all I have to do to find pages that have a specific Content (for example Page or Block) referenced in a ContentArea is this:

string id = ID-TO-YOUR-CONTENT;
PropertyCriteriaCollection criterias = new PropertyCriteriaCollection
{
    new PropertyCriteria
        {
            Condition = CompareCondition.Contained,
            Name = "ResponsibleEditors",
            Required = true,
            Type = PropertyDataType.String,
            Value = string.Format("data-contentlink=\"{0}\"", id)
        }
};
IPageCriteriaQueryService queryService = ServiceLocator.Current.GetInstance<IPageCriteriaQueryService>();
PageDataCollection pages = queryService.FindPagesWithCriteria(ContentReference.StartPage, criterias);

You can also use the content GUID and use this as Value: string.Format(“data-contentguid=\”{0}\””, guid)

As usual, if you want to use FindPagesWithCriteria, remember to put a cache on it since it can be quite heavy on the database.