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.
Comments