EPiServer 8 and IncludeOn

Vote:
 

Hi,

I have the following page types:

- ContainerPage
- A
- B
- C

Container page can contain any page type.
A page can contain B and C page types
B can contain B and C page types
C cannot contain any page type.

Here's the code:

[ContentType(GUID = "21884d6e-c1cf-4124-bc03-a06aed328780")]
public class ContainerPage : PageData
{
}

[ContentType(GUID = "23d1f5fa-7766-421d-9376-ed7873d6f1ee")]
[AvailableContentTypes(
    Availability.Specific,
    Include = new[] { typeof(B), typeof(C) })]
public class A : PageData
{
}

[ContentType(GUID = "99853a59-1496-4b20-9a15-d993ea63a7f8")]
[AvailableContentTypes(
    Availability.Specific,
    Include = new[] { typeof(B), typeof(C) },
    IncludeOn = new[] { typeof(A) })]
public class B : PageData
{
}

[ContentType(GUID = "85eceadb-bf58-4b0d-b82d-4c08daaaa683")]
[AvailableContentTypes(Availability.None)]
public class C : PageData

The problem is, editors are allowed to create B pages types under ContainerPage.

Is this a bug with IncludeOn attribute, or is this how it's supposed to work?
Do I have to specify all AvailableContentTypes on ContainerPage or can I solve this in another way?

EPiServer 8.2.0

Thanks!

#119194
Mar 23, 2015 15:53
Vote:
 

There's no edit button for original question :(

I know I can use Exclude flag on parent page types, and even extract some interface in order to simplify the list of included / excluded content types... but I have too many page types, and I'd like to keep this DRY.

Is this possible? :)

#119195
Mar 23, 2015 16:15
Vote:
 

Hi

To me this sounds like a bug. None of your speciified setting should affect the settings for ContainerPage. I will report a bug on this.

#119199
Mar 24, 2015 9:14
Vote:
 

Thanks!

#119200
Mar 24, 2015 9:32
Vote:
 

I looked at the bug but I can't really figure out what you mean, why are you using IncludeOn=B?

If I change your code above using the ExcludeOn=ContainerPage it is correctly exluded from container page. If I use IncludeOn then it is included.

 [ContentType(GUID = "99853a59-1496-4b20-9a15-d993ea63a7f8")]
    [AvailableContentTypes(
        Availability.Specific,
        Include = new[] { typeof(B), typeof(C) },
        ExcludeOn = new[] { typeof(ContainerPage) })]
    public class B : PageData
    {
    }
#119299
Mar 26, 2015 14:17
Vote:
 

Sorry, it might not be a bug :)

If I want to include page type B on page type A, and exclude it from all other page types, do I have to use

[ContentType(GUID = "...")]
[AvailableContentTypes(
    Availability.Specific,
        ExcludeOn = new[] { typeof(...), typeof(...), typeof(...), typeof(...) ... })]
public class B : PageData
{
}

or can I just use:

[ContentType(GUID = "...")]
[AvailableContentTypes(
    Availability.Specific,
        IncludeOn = new[] { typeof(A))]
public class B : PageData
{
}

I thought if I only specify IncludeOn = 'something', that's equivalent to IncludeOn = 'something' and ExludeOn = All

#119303
Mar 26, 2015 14:49
Vote:
 

Ok, IncludeOn does not exclude on other types.

#119307
Mar 26, 2015 15:43
Vote:
 

Basically I don't want to update ExcludeOn list each time I create a new page type.
If I exclude PageDate like this:

[ContentType(GUID = "...")]
[AvailableContentTypes(Availability.None,
    ExcludeOn = new[] { typeof(PageData) },
    IncludeOn = new[] { typeof(A) })]
public class B : PageData
{
}

Then I cannot create page type B under page type A.

edit: Ok, now I see what I want to achieve is not possible. Thanks Per!

#119309
Edited, Mar 26, 2015 15:46
Vote:
 

I ran into this when creating a module which had two page types: X and Y

Now I want X to be created only directly under the root page, and Y only created under X, like so:

	[ContentType]
	[AvailableContentTypes(Include = new[] { typeof(Y) }, ExcludeOn = new[] { typeof(PageData) }, Availability = Availability.Specific)]
	public class X : PageData
	{
	}

	[ContentType]
	[AvailableContentTypes(IncludeOn = new[] { typeof(X), typeof(Y) }, Availability = Availability.Specific)]
	public class Y : PageData
	{
	}

This work alright, except ofcourse Y will be able to be created on all current and future content types that does not have the [AvailableContentTypes(Include = ...)] attribute. As the defult is to have all content types available.

I did some reflecting and came up with this, but I am worried it can have unforeseen consequences, though in the interest of getting an official option to exclude a content type everywhere except where I want, here goes:

	/// <summary>
	/// Modifies the <see cref="ContentTypeModel.AvailableContentTypes"/> setting for all content types to exclude the <see cref="Y"/> type.
	/// </summary>
	public class SetContentTypeAvailability : ContentDataAttributeScanningAssigner
	{
		public override void AssignValues(ContentTypeModel contentTypeModel)
		{
			base.AssignValues(contentTypeModel);

			if (contentTypeModel.ModelType == typeof (X) || contentTypeModel.ModelType == typeof (Y)) {
				return;
			}

			if (contentTypeModel.AvailableContentTypes == null) {
				contentTypeModel.AvailableContentTypes = new AvailableContentTypesAttribute();
			}
			if (contentTypeModel.AvailableContentTypes.Exclude.All(t => t != typeof (Y))) {
				contentTypeModel.AvailableContentTypes.Exclude = contentTypeModel.AvailableContentTypes.Exclude.Union(new[] {typeof (Y)}).ToArray();
			}
		}
	}

	[InitializableModule]
	[ModuleDependency(typeof (InitializationModule), typeof (ServiceContainerInitialization))]
	public class Initializer : IConfigurableModule
	{
		public void Initialize(InitializationEngine context)
		{
		}
		public void UnInitialize(InitializationEngine context)
		{
		}

		public void ConfigureContainer(ServiceConfigurationContext context)
		{
			context.Container.Configure(ctx => ctx.For<IContentTypeModelAssigner>().Use<SetContentTypeAvailability>());
		}
	}

A neater option would be something maybe like so, where the Explicit enum would mean "ignore everything else!":

	[ContentType]
	[AvailableContentTypes(IncludeOn = new[] { typeof(Y), typeof(X) }, Availability = Availability.Explicit)]
	public class Y : PageData
	{
	}
#133172
Edited, Aug 27, 2015 11:35
Vote:
 

I know it's a bit late, but I have just been fixing an issue with this attribute as reported in http://world.episerver.com/blogs/Henrik-Fransas/Dates/2015/10/the-known-and-unknown-of-include-includeon-exclude-and-excludeon/ and though I would have a look into your suggestion Erik.

My suggestion in your case would be to take a step back and instead of trying too hard with the attributes and instead work directly with the IAvailableModelSettingsRepository and Register a setting there. This is the equivalent of changing the setting in the Admin UI, so the result is that new content types won't be included unless explicitly stated so. The downside of this is that you would have to take special care if you still want administrators to be able to manipulate these values.

#140737
Oct 28, 2015 20:48
Vote:
 

I have the same issue, I would prefer a better solution for this scenario. A simple practical scenario is a news archive based on two page types. "NewsPageData" should only be available on "NewsListPageData", but unavailable for all other page types.

#173508
Edited, Dec 30, 2016 10:56
Vote:
 

Hah! This old thread.

I actually did take a satep back and scrapped the whole idea at first. But I now revisited the IAvailableModelSettingsRepository idea from Henriks comment.

The problem with this approach is that you can not register settings specifically for ex. the root page due to it having no ModelType, atleast not from what I can see. Though that will be a minor issue for my specific scenario as editors aren't usually allowed to create pages under the root page directly anyways.

This is what I ended up with in the end:

 private void InitializeContentTypeRestrictions()
	    {
            // load all content types registered
            var contentTypeRepository = ServiceLocator.Current.GetInstance<IContentTypeRepository>();
            var allContentTypes = contentTypeRepository.List().Where(c => c.ModelType != null).Select(c => c.ModelType).ToList();
            
            var availableModelSettingsRepository = ServiceLocator.Current.GetInstance<IAvailableModelSettingsRepository>();
            
            // create and register setting for TranslationStringPage
	        var setting = new AvailableContentTypesAttribute(Availability.Specific) {
	            ExcludeOn = allContentTypes.Except(new[] {typeof(TranslationStringPage), typeof(TranslationRootPage)}).ToArray(),
	            Include = new[] {typeof(TranslationStringPage)}
	        };
	        availableModelSettingsRepository.RegisterSetting(typeof(TranslationStringPage), setting);

            // create and register setting for TranslationRootPage
	        setting = new AvailableContentTypesAttribute(Availability.Specific) {
	            ExcludeOn = allContentTypes.ToArray(),
	            Include = new[] {typeof(TranslationStringPage)}
	        };
	        availableModelSettingsRepository.RegisterSetting(typeof(TranslationRootPage), setting);
	    }
#173601
Edited, Jan 03, 2017 13:22
Vote:
 

Very old post indeed haha, a more "native" solution would still be prefered in the long run of course :)

#173615
Jan 03, 2017 15:38
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.