The new and improved allowed types
TL;DR
The latest pre-release version of the EPiServer.CMS.UI package contains the new and improved allowed types attribute. If you are feeling adventurous then upgrade and try it out. The improvements are:
- Clicking the "create a new block" link of a content area will now filter the block types that appear based on allowed types.
- It is now possible to restrict a subset of the allowed types.
- Interface types are now supported.
- UI descriptors are now automatically generated for content types classes and their base classes.
- Validation occurs for allowed types in the content area.
AllowedTypesAttribute
Most of you have probably read previous blog posts from myself and others on the allowed types attribute and its usages and its limitations. For those who don't know what it is, it is an attribute that can be applied to properties on your content type model in order to control what content types that property can accept. For example, only allowing images on a gallery property. The code looks like this:
[AllowedTypes(new []{ typeof(ImageData) })]
public virtual ContentArea Gallery { get; set; }
In the next major version of EPiServer (EPiServer.CMS.UI 8.0.0) we have made a lot of improvements to the allowed types in order to support requested features and fix problems in the current version. The next version is currently available in the nuget feed as a pre-release package if you are interested to give it a test run.
Creating Content from the Content Area
The biggest problem with the current implementation of the allowed types, and also the most up-voted bug on EPiServer World (110190), is the fact that allowed types doesn't filter the list of content types when creating a block from a content area; it only affected drag and drop. This has now been fixed meaning that it is no longer possible to add disallowed types to the content area either via drag and drop or the create link.
Validation of Allowed Types
The content area has also been modified in the core so that, when saving or publishing, it validates whether it contains any types that are not allowed. This ensures that changes to a content area via code also follow the allowed types restrictions.
Restricted Types
An additional feature we have added to the allowed types attribute is the ability to specify a subset of types that should not be allowed. For example, it is quite common to have a content area which should accept all blocks except for blocks which don't fit; i.e. the jumbotron block in the alloy templates is too big to be rendered in the sidebar. The restricted types are passed through as the second constructor argument on the allowed types attribute. The code looks like this:
[AllowedTypes(new[] { typeof(BlockData) }, new[] { typeof(JumbotronBlock) })]
public virtual ContentArea RelatedContentArea { get; set; }
Automatic Generation of UI Descriptors
Currently the system automatically creates UI descriptors for all the registered content types. This means that you can specify a particular content type as an allowed or restricted type without the need to create the UI descriptor yourself. However this was not the case for base classes and thus you needed to create a UI descriptor for any base classes you wanted to specify in the allowed types. In the next version we have refactored the automatic generation so that base classes are also generated, meaning that you can specify any class type without needing to create a UI descriptor yourself.
If you have a type that you want to create a UI descriptor for in order to, for example, have a custom icon. Then you can still create the UI descriptor and it will take precedence over the automatically generated one.
Interface Support
We have also made changes to support UI descriptors for interfaces, meaning that it is possible to specify an interface type as the allowed type. For example, if you had an interface for blocks that should appear in the sidebar then the code would look like this:
[AllowedTypes(new[] { typeof(ISidebarBlock) })]
public virtual ContentArea RelatedContentArea { get; set; }
However, unlike the content type classes and their base classes, UI descriptors are not automatically generated. So in order for the above to work you will need to create a UI descriptor.
How about creating private blocks for/under a page; is the list of available block types filtered according to the AvailableContentTypesAttribute on the page type definition? That would be super sweet, now that we can let editors create private blocks for pages.
Do you mean creating blocks directly under a page? We don't have support for that. Local blocks are still created under the local folder in the content assets tree. The list of available types is filtered based on the allowed types attribute only since available types only.
EPiServer.CMS.UI 8.0.0-pre-000379, blocks are not filtered. I cleared the browser cache, and restarted the IIS.
Sorry, seems I was a bit eager to get the blog post up. The latest pre-release package (EPiServer.CMS.UI 8.0.0-pre-000384) is now available on the nuget server.
@Ben, yes. When an editor wants to create a block that belongs to a page, every single block type is presented as an option, and that is not editor friendly at all in a solution with many block type definitions. It would be nice if the list of available blocks could be filtered, the same way available page types is when creating new pages. From code it would be natural to define this kind of filtering using the existing AvailableContentTypesAttribute.
As I understand it, private blocks are created under a page in a ContentAssetFolder. The ContentAssetFolder has a property called ContentOwnerId, which is a reference to the page. With that one could do filtering using an instance of the IAvailableContentTypes's ListAvailable method. This needs to happen in the RestStore named "contenttype", I imagine. I did some reflection on the rest store class ContentTypeStore, and this is where this needs to be implemented as far as I can tell.
@Ben, pre-000384 throws the following exception: "AllowedTypesAttribute is only supported on ContentReference,ContentArea,LinkItemCollection". I need to use the AllowedTypes attribute on XhtmlString field as well.
@Dejan: As the error message states, there is currently no support to handle this for an XHtmlString. Actually, the error message is even a bit faulty since the UI does not handle LinkCollections in a good way either. The problem with both Link Collections and XHtmlString is that they are a bit more advanced than just a contentreference (or list of content references as the content area can be treated as). Not saying that it's not possible to add support, but it's a bit more complex and I think that we need more feedback to be able to implement it in a good way.
Let's take some examples for an XHtmlString just to bring up questions that needs to be answered to be able to implement support for an XHtmlString in a good way. Let's say that you annotate an XHtmlString with AllowedTyped(new Type[]{typeof(SomePage), typeof(SomeBlock)}). This raises some questions:
1. Should it be allowed to create an external link in the editor?
2. Should it be able to create a link to content items that have a URL that are not type of (SomePage)?
3. Should you be able to add links/inline media (for instant images) that are not part of the AllowedTypes?
I'm assuming that the most commonly desired behavior is to still allow links and media to be added to the XhtmlString, and just preventing which content types can be added as inline content blocks but it would be nice to get this verified.
@Linus, thanks for reply.
I was pleased with how AllowedTypes worked with XhtmlString in EPi 7.19.2, and EPiServer.CMS.UI 8.0.0-pre-000379.
If you could keep the existing functionality in EPi 8, and improve small drag&drop glitches in Chrome/Firefox, that would be perfect :)
Should it be allowed to create an external link in the editor? Yes
Should it be able to create a link to content items that have a URL that are not type of (SomePage)? Yes
Should you be able to add links/inline media (for instant images) that are not part of the AllowedTypes? I would say yes :)
"I'm assuming that the most commonly desired behavior is to still allow links and media to be added to the XhtmlString, and just preventing which content types can be added as inline content blocks but it would be nice to get this verified."
That would be great
Maybe I'm a bit confused Dejan, but AllowedTypes has never worked with XhtmlString. How did you get that to work?
@Johan,
I've created a new EPiServer MVC website using Visual Studio extension, upgraded EPi nuget packages to 7.19+, and simply used AllowedTypes on XhtmlString.
Here's how it works: http://postimg.org/image/6zvqkk2pb/
Tested on Windows and Mac.
Ah nice. Never considered AllowedTypes for XhtmlString, but I guess that makes sense. We usually try to stay away from dropping content into XhtmlStrings.
On a related note. Would be nice if DefaultDragAndDropTarget worked for ContentAreas, now it only seems to work for ContentReference. This was reported as a bug (Incident DI-142361), but was dismissed since someone couldn't reproduce the problem, even though the support team was able to.
I'm trying to work my way away from Dynamic Content plugins and rather use specific blocks in XhtmlString, but I'd rather not open up for all block types. So a way of restricting what blocks types should be allowed in a XhtmlString would be great.
Although... I'm sure the editors would be thrilled over being able to drag-n-drop all kind of stuff into whereever they'd like... :-)
How do you use the new EPiServer.Forms with AllowedTypes?
Aka
@Patrick Nodrin,
[AllowedTypes(typeof(FormContainerBlock))]