Limit a Content Area Field Allowed Types in the Publishing Event in EPiServer CMS

The allowed types attribute in a content area is a useful way to restrict which blocks the editor can set in a particular content area field. However, sometimes the user wants to decide which kind of block types to be allowed in the content area field dynamically without having to modify the code. This is a first attempt, to try to do this. However, I have to warn all of you that it is not perfect and has a huge drawback. It requires to publish the page or block with the allowed type control field, before the content area can validate properly the items.

epi_server_logo_detail

First, we will create a selection factory where we will set the document types that a document block can set and this will work as the control field for the content area items in a document group block


    public class TypeSelectionFactory : ISelectionFactory
    {
        private readonly Injected ContentRepository;

        public IEnumerable GetSelections(ExtendedMetadata metadata)
        {
            var settings = ContentRepository.Service.Get(ContentReference.StartPage);
            var items = new List();

            items.Add(new SelectItem { Text = "Document 1", Value = "Document 1" });
            items.Add(new SelectItem { Text = "Document 2", Value = "Document 2" });

            return items;
        }
    }

Then, we define a document block with three fields: a title, a teaser and the document type which will be a select one type selection factory field as defined above.


    [ContentType(GUID = "3014e7d1-53f3-49ca-94ff-29d3da5101a4")]
    public class DocumentBlock : BlockData
    {
        [CultureSpecific]
        [Display(GroupName = SystemTabNames.Content, Order = 100)]
        public virtual string DocumentTitle { get; set; }
        
        [CultureSpecific]
        [UIHint(UIHint.Textarea)]
        [Display(GroupName = SystemTabNames.Content, Order = 200)]
        public virtual string Teaser { get; set; }

        [CultureSpecific]
        [Display(GroupName = SystemTabNames.Content, Order = 300)]
        [SelectOne(SelectionFactoryType = typeof(TypeSelectionFactory))]
        public virtual string Type { get; set; }
    }

Now, we will create the document group block which will contain the document type control field for the content area, the group title and the content area which allows only document blocks. The document type property is required, while the document listing is not. This allow us to specify a document type and after publishing the block, the validation can take place for the content area field.


    [ContentType(GUID = "c1a7b1d2-59ff-85fb-ba6b-b99ac9ef4ffa")]
    public class DocumentGroupBlock : BlockData
    {
        [Required]
        [CultureSpecific]
        [Display(GroupName = SystemTabNames.Content, Order = 100)]
        [SelectOne(SelectionFactoryType = typeof(TypeSelectionFactory))]
        public virtual string DocumentType { get; set; }

        [Required]
        [CultureSpecific]
        [Display(GroupName = SystemTabNames.Content, Order = 200)]
        public virtual string GroupTitle { get; set; }

        [CultureSpecific]
        [Display(GroupName = SystemTabNames.Content, Order = 300)]
        [AllowedTypes(typeof(DocumentBlock))]
        public virtual ContentArea DocumentListing { get; set; }
    }

Finally, we will add an initialization module which will be responsible of the validation process of the content area items in the document group block. We will attach to the publishing event to achieve this.


    [InitializableModule]
    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class SitemaPagePublishConfig : IInitializableModule
    {
        public IContentRepository _repository;
        private IContentEvents _contentEvents;

        public void Initialize(InitializationEngine context)
        {
            //Add initialization logic, this method is called once after CMS has been initialized
            if (_contentEvents == null)
            {
                _contentEvents = ServiceLocator.Current.GetInstance();
            }

            if (_repository == null)
            {
                _repository = ServiceLocator.Current.GetInstance();
            }

            _contentEvents.PublishingContent += ContentEvents_PublishingContent;
        }

        public void Uninitialize(InitializationEngine context)
        {
            //Add uninitialization logic
            if (_contentEvents == null)
            {
                _contentEvents = ServiceLocator.Current.GetInstance();
            }

            _contentEvents.PublishedContent -= ContentEvents_PublishingContent;
        }

        private void ContentEvents_PublishingContent(object sender, ContentEventArgs e)
        {
            if (sender == null || e == null) return;

            if (e.Content is DocumentGroupBlock)
            {
                var documentGroupBlock = _repository.Get(e.ContentLink);
                var documentType = documentGroupBlock.DocumentType;

                if (documentGroupBlock != null)
                {
                    var documents = documentGroupBlock.DocumentListing?.Items;
                    if (documents != null && documents.Any())
                    {
                        foreach (var document in documents)
                        {
                            var documentData = _repository.Get(document.ContentLink);

                            if (documentData != null)
                            {
                                if (string.IsNullOrEmpty(documentData.Type) || documentData.Type != documentType)
                                {
                                    e.CancelAction = true;
                                    e.CancelReason = "Not valid document types in the content area field";
                                    return;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

The initialization module above will validate the items in the content area every time the editor tries to publish the block. If it fails it will send the message in the cancel reason property from the ContentEventArgs class. This will provide some feedback to the user about why the publishing event failed.

And that is all. As mentioned in the beginning of this post, this is just a test approach and may not be enough to fulfill the specifications of a client. I hope it will help someone and as always keep learning !!!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s