Views: 1349
Number of votes: 0
Average rating:

Page Constraints

I have built a small plug-in that 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).

   1: [PageType("73312396-6092-475f-9f3a-e5be1adfcf95", Filename = "~/Templates/Pages/Author.aspx")]
   2: public class AuthorPage : TypedPageData
   3: {
   4:     [Required(ErrorMessage = "name cant be empty")]
   5:     [PageTypeProperty(Type = typeof(PropertyString))]
   6:     public virtual string Name { get; set; }
   7:  
   8:     [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")]
   9:     [PageTypeProperty(Type = typeof(PropertyString))]
  10:     public virtual string WebPage { get; set; }
  11:  
  12:     [Range(18, 130, ErrorMessage = "author must be between 18 and 130 years old")]
  13:     public virtual int? Age { get; set; }
  14: }

 

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.

   1: public class UrlResolvableAttribute : RegularExpressionAttribute
   2: {
   3:     public string Url { get; set; }
   4:  
   5:     public UrlResolvableAttribute(string pattern) : base(pattern) { }
   6:  
   7:     public override bool IsValid(object value)
   8:     {
   9:         if (value == null) return true;
  10:  
  11:         var valid = base.IsValid(value);
  12:         if (!valid)
  13:             return false;
  14:  
  15:         string url;
  16:         if (!GetUrl(value, out url)) return false;
  17:  
  18:         Uri uri;
  19:         if (!GetUri(url, out uri)) return false;
  20:  
  21:         var request = WebRequest.Create(uri.ToString());
  22:         try
  23:         {
  24:             using (var reponse = (HttpWebResponse)request.GetResponse())
  25:             {
  26:                 if (reponse == null)
  27:                 {
  28:                     return false;
  29:                 }
  30:  
  31:                 var statusCode = reponse.StatusCode;
  32:                 return statusCode == HttpStatusCode.OK;
  33:             }
  34:         }
  35:         catch (WebException ex)
  36:         {
  37:             if (ex.Status == WebExceptionStatus.ProtocolError)
  38:             {
  39:                 var response = ex.Response as HttpWebResponse;
  40:                 if (response != null && response.StatusCode != HttpStatusCode.OK)
  41:                 {
  42:                     return false;
  43:                 }
  44:             }
  45:         }
  46:  
  47:         return false;
  48:     }
  49:  
  50:     private bool GetUrl(object value, out string url)
  51:     {
  52:         url = value != null ? value.ToString() : string.Empty;
  53:         if (string.IsNullOrEmpty(url))
  54:         {
  55:             return false;
  56:         }
  57:         return true;
  58:     }
  59:  
  60:     private bool GetUri(string url, out Uri uri)
  61:     {
  62:         uri = null;
  63:         try
  64:         {
  65:             uri = new Uri(url);
  66:         }
  67:         catch (Exception)
  68:         {
  69:             return false;
  70:         }
  71:         return true;
  72:     }
  73: }

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.

   1: [PageType("cfe42b48-496c-475e-86db-c2030175bc83", Filename = "~/Templates/Pages/Book.aspx")]
   2: public class BookPage : TypedPageData, IConstraintPage
   3: {
   4:     [Required(ErrorMessage = "book must have a name")]
   5:     [StringLength(30, ErrorMessage = "book cant have a name longer that 10 chars")]
   6:     [PageTypeProperty(Type = typeof(PropertyString))]
   7:     public virtual string Name { get; set; }
   8:  
   9:     [Required(ErrorMessage = "isbn cant be empty")]
  10:     [RegularExpression(@"^(?=[-0-9xX ]{13}$)(?:[0-9]+[- ]){3}[0-9]*[xX0-9]$", ErrorMessage = "isbn is not valid")]
  11:     [PageTypeProperty(Type = typeof(PropertyString))]
  12:     public virtual string Isbn { get; set; }
  13:  
  14:     [Required(ErrorMessage = "book must have authors")]
  15:     [PageTypeProperty(Type = typeof(PropertyLinkCollection))]
  16:     public virtual LinkItemCollection Authors { get; set; }
  17:  
  18:     public bool IsViolated(out string reason)
  19:     {
  20:         reason = string.Empty;
  21:         if (!string.IsNullOrEmpty(Name) && Name.Contains("twilight"))
  22:         {
  23:             reason = "this bookstore does not allow the twilight books";
  24:             return true;
  25:         }
  26:         return false;
  27:     }
  28: }

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.

May 24, 2012

Please login to comment.