如何限制内容区域中使用的块类型的数量?
How to restrict the number of a blocktype used in contentarea?
我有一个块类型,用于特定页面的特定内容区域。有什么方法可以验证(在页面级别或内容区域级别)该块未被多次使用?
没有内置内容,但您可以轻松连接到 SavingContent
或 PublishingContent
事件并在 saved/published 之前验证内容。
这是一个示例验证属性 class,应该会有帮助。我正在开发一个 "Validation Rules" nuget 包,我认为它可以包括这个。我只包含了 "Min by object type" 规则,但在发布之前会添加更多规则。
Class:
using EPiServer;
using EPiServer.Core;
using EPiServer.ServiceLocation;
using System;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
namespace eGandalf.Epi.Validation.Lists
{
/// <summary>
/// Detects whether the minimum required items of a specific type within a ContentArea condition has been met. Only supports items that can be loaded by IContentLoader. Supports type inheritance.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = true)]
public class MinimumOfTypeAttribute : ValidationAttribute
{
public int Limit { get; }
public Type ObjectType { get; }
public MinimumOfTypeAttribute(int limit, Type t)
{
Limit = limit;
ObjectType = t;
}
public override bool IsValid(object value)
{
if (value == null && Limit > 0) return false;
var area = value as ContentArea;
if (area != null) return ValidateContentArea(area);
throw new TypeMismatchException("Minimum of type only works with ContentArea properties.");
}
private bool ValidateContentArea(ContentArea area)
{
if (area?.Items?.Count < Limit) return false;
var typeCount = 0;
foreach (var item in area.Items)
{
if (CanLoadContentByType(item.ContentLink))
{
typeCount++;
// Return as soon as the validation is true.
if (typeCount >= Limit) return true;
}
}
return false;
}
private bool CanLoadContentByType(ContentReference reference)
{
var loader = ServiceLocator.Current.GetInstance<IContentLoader>();
var loaderType = loader.GetType();
MethodInfo getMethod = loaderType.GetMethod("Get", new Type[] { typeof(ContentReference) });
MethodInfo genericGet = getMethod.MakeGenericMethod(new[] { ObjectType });
try
{
var content = genericGet.Invoke(loader, new object[] { reference });
return content != null;
}
catch (Exception ex)
{
return false;
}
}
public override string FormatErrorMessage(string name)
{
return $"ContentArea {name} must include at least {Limit} items of type {ObjectType.Name}";
}
}
}
内容区域的示例应用程序:
[MinimumOfType(1, typeof(RssReaderBlock))]
public virtual ContentArea RelatedContentArea { get; set; }
无效时在编辑器视图中的结果(阻止发布):
我有一个块类型,用于特定页面的特定内容区域。有什么方法可以验证(在页面级别或内容区域级别)该块未被多次使用?
没有内置内容,但您可以轻松连接到 SavingContent
或 PublishingContent
事件并在 saved/published 之前验证内容。
这是一个示例验证属性 class,应该会有帮助。我正在开发一个 "Validation Rules" nuget 包,我认为它可以包括这个。我只包含了 "Min by object type" 规则,但在发布之前会添加更多规则。
Class:
using EPiServer;
using EPiServer.Core;
using EPiServer.ServiceLocation;
using System;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
namespace eGandalf.Epi.Validation.Lists
{
/// <summary>
/// Detects whether the minimum required items of a specific type within a ContentArea condition has been met. Only supports items that can be loaded by IContentLoader. Supports type inheritance.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = true)]
public class MinimumOfTypeAttribute : ValidationAttribute
{
public int Limit { get; }
public Type ObjectType { get; }
public MinimumOfTypeAttribute(int limit, Type t)
{
Limit = limit;
ObjectType = t;
}
public override bool IsValid(object value)
{
if (value == null && Limit > 0) return false;
var area = value as ContentArea;
if (area != null) return ValidateContentArea(area);
throw new TypeMismatchException("Minimum of type only works with ContentArea properties.");
}
private bool ValidateContentArea(ContentArea area)
{
if (area?.Items?.Count < Limit) return false;
var typeCount = 0;
foreach (var item in area.Items)
{
if (CanLoadContentByType(item.ContentLink))
{
typeCount++;
// Return as soon as the validation is true.
if (typeCount >= Limit) return true;
}
}
return false;
}
private bool CanLoadContentByType(ContentReference reference)
{
var loader = ServiceLocator.Current.GetInstance<IContentLoader>();
var loaderType = loader.GetType();
MethodInfo getMethod = loaderType.GetMethod("Get", new Type[] { typeof(ContentReference) });
MethodInfo genericGet = getMethod.MakeGenericMethod(new[] { ObjectType });
try
{
var content = genericGet.Invoke(loader, new object[] { reference });
return content != null;
}
catch (Exception ex)
{
return false;
}
}
public override string FormatErrorMessage(string name)
{
return $"ContentArea {name} must include at least {Limit} items of type {ObjectType.Name}";
}
}
}
内容区域的示例应用程序:
[MinimumOfType(1, typeof(RssReaderBlock))]
public virtual ContentArea RelatedContentArea { get; set; }
无效时在编辑器视图中的结果(阻止发布):