PAGE CONSTRAINTS

I have built a plug-in which I call PageConstraints. The plugin adds support for adding constraints to PageTypeBuilder page types. Constraints can be added with .NETs built-in DataAnnotations or by implementing custom code.

For example. Following page type is a regular PTB page type with built-in DataAnnotation attributes and a custom Attribute (UrlResolvable).

[PageType("73312396-6092-475f-9f3a-e5be1adfcf95", Filename = "~/Templates/Pages/Author.aspx")]
public class AuthorPage : TypedPageData  
{
   [Required(ErrorMessage = "name cant be empty")]
   [PageTypeProperty(Type = typeof(PropertyString))]
   public virtual string Name { get; set; }

   [UrlResolvable(@"^http\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?$", ErrorMessage = "could not resolve remote url or remote server did not return status code 200")]
   [PageTypeProperty(Type = typeof(PropertyString))]
   public virtual string WebPage { get; set; }

   [Range(18, 130, ErrorMessage = "author must be between 18 and 130 years old")]
   public virtual int? Age { get; set; }
}

Custom attributes

Its quite simple to implement a custom DataAnnotation attribute that you can add to your page type. Following Attribute is checking if a entered url is a valid uri, matching a regular expression and if the remote server of the entered url is returning a response with http status 200 by extending build-in RegularExpressionAttribute. You can see how it can be used with WebPage property at AuthorPage.

public class UrlResolvableAttribute : RegularExpressionAttribute  
{
    public string Url { get; set; }
    public UrlResolvableAttribute(string pattern) : base(pattern) { }

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

        var valid = base.IsValid(value);
        if (!valid) return false;

        string url;
        if (!GetUrl(value, out url)) return false;

        Uri uri;
        if (!GetUri(url, out uri)) return false;

        var request = WebRequest.Create(uri.ToString());
        try
        {
           using (var reponse = (HttpWebResponse)request.GetResponse())
           {
               if (reponse == null)
               {
                   return false;
               }

              var statusCode = reponse.StatusCode;
              return statusCode == HttpStatusCode.OK;
           }
        }
        catch (WebException ex)
        {
           if (ex.Status == WebExceptionStatus.ProtocolError)
           {
               var response = ex.Response as HttpWebResponse;
               if (response != null && response.StatusCode != HttpStatusCode.OK)
               {
                   return false;
               }
           }
        }

        return false;
    }

    private bool GetUrl(object value, out string url)
    {
       url = value != null ? value.ToString() : string.Empty;
       if (string.IsNullOrEmpty(url))
       {
           return false;
       }
       return true;
    }

    private bool GetUri(string url, out Uri uri)
    {
       uri = null;
       try
       {
           uri = new Uri(url);
       }
       catch (Exception)
       {
           return false;
       }
       return true;
    }
}

Custom constraint

If you need to add custom code that is not supported by DataAnnotations for the constraint with a context of the current page, implement the method IsViolated from interface IConstraintPage.

[PageType("cfe42b48-496c-475e-86db-c2030175bc83", Filename = "~/Templates/Pages/Book.aspx")]
public class BookPage : TypedPageData, IConstraintPage  
{
    [Required(ErrorMessage = "book must have a name")]
    [StringLength(30, ErrorMessage = "book cant have a name longer that 10 chars")]
    [PageTypeProperty(Type = typeof(PropertyString))]
    public virtual string Name { get; set; }

    [Required(ErrorMessage = "isbn cant be empty")]
    [RegularExpression(@"^(?=[-0-9xX ]{13}$)(?:[0-9]+[- ]){3}[0-9]*[xX0-9]$", ErrorMessage = "isbn is not valid")]
    [PageTypeProperty(Type = typeof(PropertyString))]
    public virtual string Isbn { get; set; }

    [Required(ErrorMessage = "book must have authors")]
    [PageTypeProperty(Type = typeof(PropertyLinkCollection))]
    public virtual LinkItemCollection Authors { get; set; }

    public bool IsViolated(out string reason)
    {
      reason = string.Empty;
      if (!string.IsNullOrEmpty(Name) && Name.Contains("twilight"))
      {
          reason = "this bookstore does not allow the twilight books";
          return true;
      }
      return false;
    }
}

Error messages are displayed in the standard yellow popup when trying to save or publish the page in edit mode and a EPiServerCancelException is thrown.

Source, tests, examples and compiled binary is available at GitHub. Please let me know if you think this is a needed plugin and if you have ideas for improvements.

Source is compiled with EPiServer CMS R2 (6.1.379.0) and PageTypeBuilder 2.0.

comments powered by Disqus