I am currently working on a project where it was necessary to conditionally inject some content into certain properties on some pages. Dynamic Content didn't really help me because what I wanted to do was replace entire properties with content from a property on another page and do it in a nice way for the editor. Linking the page with a shortcut to another page didn't help me because it needed to be only a partial replace and also conditional.

Here's an example - and I know there are better ways to do this but I want an example that won't compromise the Intellectual Property of what I am actually doing :)

Lets say you have a news article at http://myepisite/News/SomeArticle. The article has a nice big image on it and you want to be able to swap the image out with a different one (lets forget the fact that you'd probably just do this with two properties). You have a sub-page storing the smaller image in a property at http://myepisite/News/SomeArticle/LowImagery, which is hidden from the site. The way that you trigger the load of the low image is by URL QueryString, e.g. http://myepisite/News/SomeArticle?bandwidth=lo. (If you want some more tips on achieving this URL neatly, see my posts or other posts on URL rewriting.) Anyway, you could write a custom property type etc. to do this, but that's a fair whack of work and you'll need to create a new custom property for every content type that you might want to replace.

So how do I achieve this in a neat, generic way? EPiServer provides a nice way, but it's not the most well documented route and neither do they really recommend it. Still, I think it can work well if properly implemented. The way I implemented to achieve this was using a custom Property Get Handler.

When EPiServer asks for a page, it builds a PropertyDataCollection of all the properties on that page, along with their values. This PropertyDataCollection contains all the user-defined properties along with some that EPiServer add in as well such as PageLink, PageLanguageBranch and others. These are absolute lifesavers, although they are not well documented. The PropertyDataCollection has a default indexer which, when called, calls to a helper method to get the actual PropertyData for a property. The default helper method is a static method called 'DefaultPropertyHandler' on EPiServer.Core.PropertyGetHandler. This helper method looks for the the actual property data in four stages:

  1. It looks at the property on the current page and the current language
  2. If that is empty, it checks to see whether the property is language specific and, if it is, whether the current language is the master language. If not, then it grabs the master language page and returns the value from that property.
  3. If language isn't an issue, then it tries to get the property value from the linked page, if any.
  4. If there is not data on a linked page, then it tries to return a dynamic property value with that property name.

This works well and is the default that probably fits 99% of EPiServer installations. However, EPiServer kindly allow us to replace this default Property Get Handler. All you need to do is write your own method that matches the delegate for the handler, and then set an EPiServer property like so:

EPiServer.Core.PropertyDataCollection.GetHandler = new GetPropertyDelegate(Test.BandwidthPropertyGetHandler.BandwidthPropertyHandler);

I'm sure you can set this in most places where you need it, but I just put it into the Application_Start of Global.asax.cs. So we can see how easy it is to override the property handling, but what does our custom method actually look like? The following code is incomplete, untested and partially commented out, but it should give the idea. The two parameters coming in are the property name being queried for data and the property data collection for the current page/language. Notice that rather than use the default indexer for properties, when asking for a property data value I am explicitly calling the 'Get' method - this prevents nasty infinite loops where my custom handler keeps calling itself.

namespace Test
{
    public static class BandwidthPropertyGetHandler
    {
        // Methods
        public static PropertyData BandwidthPropertyHandler(string name, 
PropertyDataCollection properties) { // check querystring to see if we need to replace property (where possible) HttpRequest Request = System.Web.HttpContext.Current.Request; if (Request.QueryString["bandwidth"] == "lo") { // we need to swap low bandwidth if it's the right property name // you could drive this any way you like - don't hardcode it in production :) if (name == "ArticleImage") { // lets reach out and grab the data from the sub-page PageReference ThisPageReference = (PageReference)properties.
Get("PageLink").Value; PageDataCollection ChildPages = EPiServer.DataFactory.Instance.
GetChildren(ThisPageReference, LanguageSelector.MasterLanguage()); foreach (PageData ChildPage in ChildPages) { // find an article subpage if (ChildPage.PageTypeName.ToLower() == "article subpage") { // grab our property and return it return ChildPage.Property.Get("ArticleImageLow"); } } } } // just return the default handler return PropertyGetHandler.DefaultPropertyHandler(name, properties); } } }

In my actual implementation I handle languages properly, error handling, null property values and all sorts of other things, but it should be fairly obvious how that would work. In essence, this is a very simple way of customising properties to make them do fairly clever things without customising property types themselves. In fact, this would be a nice way to retro-fit a feature such as bandwidth tailoring or other targeted content to an existing type. Just remember that if it's not a special case you should be handling, call the default handler to do it's stuff with that property! Also, it might be best to try to keep your injected property types the same as the property type on the owner page. Not sure what would happen if you messed with that.


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist
posted @ Tuesday, December 09, 2008 12:42 PM | in .NET/C# Techie EPiServer

Comments

Gravatar
# re: Property Data Injection in EPiServer
Posted by Steve Celius
on 12/9/2008 6:37 PM
Nice! For this special case, if you knew that you had low-bandwidth images, you could have changed the image path on the way out, to <name>_lo.jpg for an example.

Another use for this is to override how dynamic properties are handled. If you have page types that should not be affected by dynamic properties for an example, you could test and skip the call to base.

Using control adapters also help when you want to change how things render - without actually writing special markup for it in your aspx/ascx files. Not really applicable in the case above, but interesting never the less. See Mari's post about it: http://labs.episerver.com/en/Blogs/Mari-Jorgensen/Dates/112298/3/Replacing-Property-Rendering-Using-Adapters/

/Steve
Gravatar
# re: Property Data Injection in EPiServer
on 12/9/2008 11:16 PM
Nice post. Guess this can be used to "override" buildt in properties also.
Gravatar
# re: Property Data Injection in EPiServer
Posted by Dan Matthews
on 12/10/2008 8:42 AM
Steve: you're absolutely right, and I would do something similar - write a HTTP pipeline component to pick up img tags and amend them probably. This post was intended as a demonstration of technique rather than a real application. If you want to suggest a real application though... ;)

Anders: i guess it could! might get very interesting though :)
Gravatar
# re: Property Data Injection in EPiServer
Posted by Magnus Stråle
on 12/10/2008 12:17 PM
This is exactly the kind of scenario where you might want to use your own PropertyGetHandler. The reason for being a bit careful with using your own handler is that you might get yourself into a bit of trouble occasionally.

There are really two special cases that you need to keep in mind:

1. You custom handler is only called when the PropertyDataCollection is in ReadOnly mode.

2. When saving contents, the information is read via the PropwertyDataCollection.Get method rather than from the indexer. The Get implementation bypasses the handler completely.

/MagnusS
Chief Software Architect EPiServer
Gravatar
# Raymond Kirk
Posted by Raymond Kirk
on 4/25/2009 1:58 PM
That's great, I never thought about Property Data Injection in EPiServer like that before.

Post Comment

Title *
Name *
Email
Url
Comment *  


Please add 8 and 6 and type the answer here: