4

I'm having a issue restricting what kind of Block to be inserted in a ContentArea. What I want is that the SliderBlock's ContentArea property can only have insertion of a SlideItemBlock.

[ContentType(...)]
public class SlideItemBlock : BlockData
{
    [Required]
    Display(Name = "Image")]
    public virtual string Image { get; set;}
}

[ContentType(...)]
public class SliderBlock : BlockData
{
    [Required]
    [Display(Name = "Slides")]
    public virtual ContentArea Slides { get; set; }
    //Should only accept insertion of SlideItemBlock
}

Or is this the wrong way to achive what I'm trying to restrict for the editor to not drag and drop wrong block types?

As of now, I can create a SliderBlock and insert a SlideItemBlocks in it. If I then insert the created SliderBlock in a new SliderBlock I get a forever and ever loop and It breaks the site. This is what I'm trying to control.

Raskolnikoov
  • 565
  • 11
  • 27

5 Answers5

5

If you´re using EPiServer 7.5 restricting which blocks you can use in a content area is built in. For details take a look at this blog post: Restricting the allowed types in a content area.

Code example from the blog post:

  [EditorDescriptorRegistration(TargetType = typeof(ContentArea), UIHint = "Gallery")]
  public class ImageGalleryEditorDescriptor : EditorDescriptor
  {    
     public ImageGalleryEditorDescriptor()    
     {    
        // Setup the types that are allowed to be dragged and dropped into the content        
        // area; in this case only images are allowed to be added.        
        AllowedTypes = new Type[] { typeof(IContentImage) };         

        // Unfortunetly the ContentAreaEditorDescriptor is located in the CMS module        
        // and thus can not be inherited from; these settings are copied from that        
        // descriptor. These settings determine which editor and overlay should be        
        // used by this property in edit mode.        
        ClientEditingClass = "epi-cms.contentediting.editors.ContentAreaEditor";        
        OverlayConfiguration.Add("customType", "epi-cms.widget.overlay.ContentArea");    
    }
  }
Philip Pittle
  • 11,821
  • 8
  • 59
  • 123
4

As of EpiServer 8 theres a new attribute called [AllowedTypes]. This is now the best way of restricting blocks. It overcomes a lot of the limitations of [AvailableContentTypes]. When you drag blocks into a content area the validation actually works.

An example code snippet would be

[AllowedTypes(new []{ typeof(SlideBlock) })]
public virtual ContentArea Slides { get; set; }

Theres a good code example here How To Restrict The Blocks Allowed Within A Content Area Episerver

Also this one on EpiWorld http://world.episerver.com/blogs/Ben-McKernan/Dates/2015/2/the-new-and-improved-allowed-types/

Jon Jones
  • 1,014
  • 1
  • 9
  • 17
1

Of you have not upgraded to 7.5 yet as Frederik suggest we have the following attribute we have created to do just this.

using EPiServer.Core;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace xxx.Com.Core.Attributes
{
    [AttributeUsage(AttributeTargets.Property)]
    public class OurAvailableContentTypesAttribute : ValidationAttribute
    {
        public Type[] Include { get; set; }
        public Type[] Exclude { get; set; }

        public override bool IsValid(object value)
        {
            if (value == null)
            {
                return true;
            }

            if (!(value is ContentArea))
            {
                throw new ValidationException("OurAvailableContentTypesAttribute is intended only for use with ContentArea properties");
            }

            var contentArea = value as ContentArea;

            var notAllowedcontentNames = new List<string>();

            if (contentArea != null)
            {
                if (Include != null)
                {
                    var notAllowedContent = contentArea.Contents.Where(x => !ContainsType(Include, x.GetType()));
                    if (notAllowedContent.Any())
                    {
                        notAllowedcontentNames.AddRange(notAllowedContent.Select(x => string.Format("{0} ({1})", x.Name, x.ContentLink.ID)));
                    }
                }
                if (Exclude != null)
                {
                    var notAllowedContent = contentArea.Contents.Where(x => ContainsType(Exclude, x.GetType()));
                    if (notAllowedContent.Any())
                    {
                        notAllowedcontentNames.AddRange(notAllowedContent.Select(x => string.Format("{0} ({1})", x.Name, x.ContentLink.ID)));
                    }
                }
            }

            if (notAllowedcontentNames.Any())
            {
                ErrorMessage = "contains invalid content items :";
                foreach (var notAllowedcontentName in notAllowedcontentNames)
                {
                    ErrorMessage += " " + notAllowedcontentName + ",";
                }
                ErrorMessage = ErrorMessage.TrimEnd(',');

                return false;
            }
            return true;
        }

        private bool ContainsType(Type[] include, Type type)
        {
            return include.Any(inc => inc.IsAssignableFrom(type));
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var result = base.IsValid(value, validationContext);
            if (result != null && !string.IsNullOrEmpty(result.ErrorMessage))
            {
                result.ErrorMessage = string.Format("{0} {1}", validationContext.DisplayName, ErrorMessage);
            }
            return result;
        }
    }
}

the usage of this is then

public class OurBlock : BlockData
    {
        [CultureSpecific]
        [Editable(true)]
        [Display(Name = "",
            Description = "",
            GroupName = SiteConstants.GroupNames.ContentArea,
            Order = 1)]
        [OurAvailableContentTypes(Include = new[] { typeof(OurImageBlock)  })]
        public virtual ContentArea ImageContentArea { get; set; }

HTH

Adam

Adam Stewart
  • 1,983
  • 1
  • 18
  • 25
0

Create a validation class and implement the IValidate interface from EPiServer.validation. The validation of this is kept outside of the PageData and BlockData classes.

This should be what you are looking for

using System.Collections.Generic;
using System.Linq;
using EPiServer.Validation;

public class SliderBlockValidator : IValidate<SliderBlock>
{
    public IEnumerable<ValidationError> Validate(SliderBlock instance)
    {
        var errors = new List<ValidationError>();
        if (instance.Slides != null &&
            instance.Slides.Contents.Any(x => x.GetType().BaseType != typeof (SlideItemBlock)))
        {
            errors.Add(new ValidationError()
                {
                    ErrorMessage = "Only SlideItemBlocks are allowed in this area",
                    PropertyName = "Slides",
                    Severity = ValidationErrorSeverity.Error,
                    ValidationType = ValidationErrorType.StorageValidation
                });
        }

        return errors;
    }
}

More reading at http://sdkbeta.episerver.com/SDK-html-Container/?path=/SdkDocuments/CMS/7/Knowledge%20Base/Developer%20Guide/Validation/Validation.htm&vppRoot=/SdkDocuments//CMS/7/Knowledge%20Base/Developer%20Guide/

If you have upgraded to EPi 7.5 you can use the AllowedTypes annotation

[AllowedTypes(new [] {typeof(SlideItemBlock)})]
public virtual ContentArea Slides { get; set; }

I am unaware if you are able to customize any messages using the later solution. There are a few known limitations

  • Restriction does not work for overlays when editing on page. This is a bug that has been fixed and will be released in a patch in a few weeks.
  • No server validation. Currently, the attribute only adds restriction in the UI. We hope to be able to add support for server validation soon which would also give the posibility validate your custom properties.
  • No validation when creating local blocks in content areas. If you use the new feature to add local blocks to a content area, there is currently no filtering of the content types when you create your new block.

Read more at http://world.episerver.com/Blogs/Linus-Ekstrom/Dates/2013/12/Restriction-of-content-types-in-properties/

All in all the first solution is currently the better one.

Eric Herlitz
  • 25,354
  • 27
  • 113
  • 157
-1

You can add a validation attribute to the content area property to restrict the allowed block types. See this link for a detailed example.

Then using the AvailableContentTypes attribute you can restrict to only allow SlideItemBlock types.

    [Required]
    [Display(Name = "Slides")]
    [AvailableContentTypes(Include = new []{typeof(SlideItemBlock)})]
    public virtual ContentArea Slides { get; set; }