Frederik Vig – ASP.NET developer

Follow me

Archive for January 2010

EPiServer code walkthrough #1 – 404 handler

This is the first post in a new series called “EPiServer code walkthrough”. What I’ll do is go through one new EPiServer module in each post. Writing a little about what it does, learn by reading its code, and hopefully contributing a little back to the project, if I see any bugs or harmful/unnecessary code that is :) .

I’m a firm believer that reading code helps you become a better developer. There are lots of modules even I just recently came across, that I didn’t know existed just a few weeks back. I think I’m not the only one, especially since they are spread around on various blogs, CodePlex, EPiCode and other places. They all contain valuable code that we as EPiServer developers should read.

I’m starting with a module I personally have not used much before, but that I know a lot of people have used in their projects: the custom 404 handler.

From the project wiki page

This module has a more advanced url redirect feature than the built-in shortcut url feature in EPiServer. It handles extensions and querystring parameters too. If you have a lot of 404 errors in your logs, you can use this to redirect the user to the correct page. Especially useful if you move templates or pages around, or have just installed EPiServer and have a lot of old urls that is no longer available.

404 handlers wiki page explains in detail what you need to do to install the module, so I’m not going to repeat it here. Instead we’ll take a look at the code.

Start by doing a SVN checkout of the source code (the svn url is https://www.coderesort.com/svn/epicode/BVNetwork.404Handler/5.x/).

SVN checkout of 404 handler

When opening up the BVNetwork.404Handler folder you’ll see a readme.txt file explaining the purpose of the module, installation, configuration etc. This is something I like! Not everyone reads the wiki page, and we as developers are especially lazy when it comes to reading documentation. Having a readme.txt with everything you need makes this so much easier, and gives the module author less support questions to answer. When using a new module, always make sure to read the documentation before asking the author questions. Saves both parties time :) .

Lets open up BVNetwork.404Handler.csproj in Visual Studio.

404 handler project structure in Visual Studio

As you can see the project is structured pretty nice, with good separations between the different parts. Very good that the project uses languages files, and that they’ve included them for five different languages. Not all modules use the language files, and if they do, they seldom have any more than one update-to-date English language file. Kudos for this.

What I don’t like is the missing fnf_logo.gif image file in the Images folder. A quick search in Visual Studio shows that this file is no longer in use, and should therefore be removed from the project. A missing image file is not the end of the world, and I know people make mistakes/forget to commit all their files, but this can be very annoying and even lead to a lot of work for other developers using your code or taking over a project.

Always make sure that your project builds on other computers than your own. I recommend manually setting up the project on preferably a new computer to see that everything works as expected, you can also use the same computer, but make sure to do the normal process a new developer would use when setting up the project (new checkout etc). Especially on larger projects that have quite a few things to setup you should do this. You’ll also receive some valuable feedback if your setup process is to complex (again on larger projects this happens quite frequently). Another thing you should use is a build server. There are quite a few out there, I know CodeResort just added Bitten as their build server for CodeResort projects. In a nutshell what a build server does is make sure you’ve included all the files and resources needed to build your project, run various tests (if you have any), deploy your files, create reports, notify team members if their build fails, and a bunch of other stuff, automatically for you. I’ll try to write a blog post up with more information on build and continuous integration servers.

Lets get back to the Visual Studio and the project! One of the things you should start out with is actually making sure the project builds after doing a checkout. So, either press ctrl + shift + b or go to Build -> Build BVNetwork.404Handler. You should have a successful build.

If we take a look through the code we see that most of the code is well documented and follows a nice naming convention making it easy to know what the purpose of the class is (CustomRedirectHandler.cs, CustomRedirectCollection.cs, Custom404Handler.cs etc). But we also see a few minor things we can clean up: unused using statements, commented out code, unused field variables etc. These are minor, but still important things to get rid of. Always strive for a clean code base, commented out code is something you should never commit into a repository, the whole reason for having a source control system is so that you can look at previous changesets and their code, there is no reason to keep the commented out code there, it will only be forgotten by the developer who commented out the code, and the other developers don’t know what to do with it, and will most likely just let it be. When I come across this I show no mercy and just delete it!

You’ll also see a few System.Diagnostics.Debug.WriteLine that are commented out. The project now uses Log4Net to log errors and warnings, System.Diagnostics.Debug.WriteLine is legacy code that we can safely remove. Another thing that I prefer is using string.Empty(); instead of “”, I’ve not updated the code because I feel this is more a personal preference. Same thing when it comes to over-documenting-code, like in this example.

/// <summary>
/// The refering url
/// </summary>
public static string GetReferer(System.Web.UI.Page page)
{
    string referer = page.Request.ServerVariables["HTTP_REFERER"];
    if (referer != null)
    {
	// Strip away host name in front, if local redirect
	string hostUrl = EPiServer.Configuration.Settings.Instance.SiteUrl.ToString();
	if (referer.StartsWith(hostUrl))
	    referer = referer.Remove(0, hostUrl.Length);
    }
    else
	referer = ""; // Can't have null
    return referer;
}

The document header can safely be removed since the method name does a good job at describing what the purpose of the method is. I’ve not removed the document header, since again this is my personal preference.

A thing I just noticed is that this is actually a Visual Studio class project, and not a Web Application project. I can only guess, but this is probably a project that got upgraded from Visual Studio 2003, I’ve previously had some problem upgrading a project from Visual Studio 2003 to Visual Studio 2008. The conversion usually goes okay (just an update to the csproj file mostly), but the ProjectTypeGuids doesn’t always get added. This is easy to fix, open up BVNetwork.404Handler.csproj in notepad or another source editor, and add this line.

<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

Save, and open up the project in Visual Studio. You should now see a slightly different icon at the top of the solution explorer window.

New project icon in Visual Studio

This will allow us to add a designer file to default.aspx from the admin folder. To do this simply right-click the file in solution explorer and choose “Convert to Web Application”. We can now delete the “Web Form Designer generated code”-region.

After all these updates its very important to make sure that the project still builds and that everything functions as expected by testing it.

What I liked the most with this project is how it uses logging. I’m personally very bad at using logging, and know that not all projects use logging as much as they should. Kudos to the authors for this!

I hope you’ve learned a few new things while reading this code, and be sure to try out the 404 handler! For SEO and user experience purposes it is crucial that the users have a smooth transition. They shouldn’t even notice the change when switching to EPiServer from another CMS (that uses different urls). A 404 (file not found) error message can also quickly become a major leak of traffic for your clients site.

A developer’s guide to PageTypeBuilder

Page Type Builder allows developers to define EPiServer page types in code which eliminates the need to synchronize page types between different servers. As page types are declared in code it also enables inheritance between page types and strongly typed property access.

From the PageTypeBuilder project

Joel Abrahamsson has done a superb job with the project and is continually working on it. I’ve earlier blogged a little about the project in my Create an EPiServer site from scratch series, but not done a proper walk-through.

New page type

Creating a new page type is very easy. You create a new class, inherit from TypedPageData, and add the [PageType] attribute to the class declaration (living inside the PageTypeBuilder namespace). You have properties for setting the different page type settings.

  • AvailableInEditMode – bool (default true)
  • AvailablePageTypes – Type[]
  • DefaultArchiveToPageID – int (default -1)
  • DefaultChildSortOrder – FilterSortOrder
  • DefaultFrameID – int
  • DefaultPageName – string
  • DefaultSortIndex – int (default -1)
  • DefaultStartPublishOffsetMinutes – int
  • DefaultStopPublishOffsetMinutes – int
  • DefaultVisibleInMenu – bool (default true)
  • Description – string
  • Filename – string
  • Name – string
  • SortOrder – string (default 100)

Example

using PageTypeBuilder;
 
namespace EPiServer.Templates.Public.PageTypes
{
    [PageType(Name = "[Public] Document list", SortOrder = 1140, Filename = "/Templates/Public/Pages/DocumentList.aspx", AvailablePageTypes = new [] { typeof(Document)})]
    public class DocumentList : TypedPageData
    {
    }
}

For a nice overview of how these properties map to the UI in EPiServer admin mode see: How the PageTypeAttribute’s Properties Map to EPiServer CMS’ Admin

Adding properties

You add properties to a page type by adding new public virtual properties to the class with the [PageTypeProperty] attribute. You have properties for setting the various EPiServer property settings.

  • ClearAllLongStringSettings – bool
  • DefaultValue – string
  • DefaultValueType – DefaultValueType
  • DisplayInEditMode – bool (default true)
  • EditCaption – string
  • HelpText – string
  • LongStringSettings – EditorToolOption
  • Required – bool
  • Searchable – bool
  • SortOrder – int
  • Tab – Type
  • Type – Type
  • UniqueValuePerLanguage – bool

Example

[PageTypeProperty(
    EditCaption = "Secondary body",
    HelpText = "The contents of this property will be shown in the right column of the page, you can use both text and images for layout. Note that images should not be larger than the right area.",
    Searchable = true,
    UniqueValuePerLanguage = true,
    Type = typeof(PropertyXhtmlString),
    LongStringSettings = (
	EditorToolOption.All ^
	EditorToolOption.Font),
    Tab = typeof(Advanced))]
public virtual string SecondaryBody
{
    get;
    set;
}

This will add a new property named SecondaryBody of type PropertyXhtmlString (XHTML string (>255)), with all editor options available, except for font. Use ^ for not available, or just add all the options you like with | between.

LongStringSettings = (EditorToolOption.DynamicContent | EditorToolOption.ToggleHtml)

Only DynamicContent and ToggleHtml will now be available for editors.

UniqueValuePerLanguage = false

When setting UniqueValuePerLanguage to false, and using the auto get setter property (like above), you’ll get null values returned on properties in other languages than the master language. To fix this you can use the GetPropertyValue extension method.

[PageTypeProperty(UniqueValuePerLanguage = false)]
public string MyProperty
{
    get 
    { 
        return this.GetPropertyValue(page => page.MyProperty, true) 
    }
}

You should now get the proper value in all languages, not just in the master language.

Tabs

To create a new tab, create a new class and inherit from Tab (PageTypeBuilder.Tab). You now need to implement three abstract properties

  • Name – string
  • RequiredAccess – AccessLevel
  • SortIndex – int

Example

using EPiServer.Security;
using PageTypeBuilder;
 
namespace EPiServer.Templates.Public.Tabs
{
    public class Advanced : Tab
    {
        public override string Name
        {
            get { return "Advanced"; }
        }
 
        public override AccessLevel RequiredAccess
        {
            get { return AccessLevel.Edit; }
        }
 
        public override int SortIndex
        {
            get { return 20; }
        }
    }
}

You can now add properties to this tab by setting the PageTypeProperty’s Tab property.

[PageTypeProperty(Tab = typeof(Advanced))]
public virtual string MyProperty
{
    get; set;
}

Dynamic properties

At the moment you cannot create dynamic properties with PageTypeBuilder. You can however access them.

public virtual string MetaDescription
{
    get
    {
	return this.GetPropertyValue(page => page.MetaDescription, true);
    }
}

As you’ll see when going through EPiServer public templates with PageTypeBuilder code, I’ve added the dynamic properties to a base class that the other page types inherit from. That way all page types have access to the dynamic properties.

Also be sure to read: Working with Dynamic Properties and Page Type Builder.

Accessing properties in templates

One of the great things about the PageTypeBuilder is that you not get strongly typed access to your properties.

Web forms (.aspx)

Inherit from the generic TemplatePage (living inside the PageTypeBuilder.UI namespace), where the type is set to the page type this template is for.

using PageTypeBuilder.UI;
 
namespace EPiServer.Templates.Public.Pages
{
    public partial class Document : TemplatePage<PageTypes.Document>
    {
    }
}

When typing CurrentPage. you’ll get access to all the public properties that Document exposes. CurrentPage.SecondaryBody for instance.

User controls (.ascx)

Inherit from the generic UserControlBase (living inside the PageTypeBuilder.UI namespace), where the type is set to the page type this user control is for. If the user control is used by multiple page types, simply find a common class to set as the type (a base class).

using PageTypeBuilder.UI;
 
namespace EPiServer.Templates.Public.Units.Placeable
{
    public partial class Document : UserControlBase<PageTypes.BasePageData>
    {
    }
}

Same as with web forms, you’ll get access the the properties that BasePageData exposes.

EPiServer public templates with PageTypeBuilder

I’ve started updating the EPiServer public templates to use PageTypeBuilder. This is not a complete conversion, but merely a nice starting place for you to see how I’ve structured my files and how the code looks when used with an existing project.

Download the code

Other resources

2010

Happy new year everyone! Just got back from an excellent vacation in New York, USA. Feel very rested and looking forward to getting back to work! Some updates: I received the EMVP (EPiServer Most Valued Professional) status, started out on my own as a freelance ASP.NET developer, and created the first EPiServer meetup group in Norway.

The biggest change for me was starting out on my own. I’m about to sign a contract for three months with a local EPiServer partner here in Oslo, for them to contract me 3-4 days a week. The other 1-2 days I’m going to spend working on an EPiServer module (more information to come) for EPiServer CMS 6.

My goal for this blog is to roughly write a blog post once a week (sometimes more, sometimes less). Mostly focusing on ASP.NET and EPiServer stuff, but also more content on other subjects, reflecting what I’m focusing on at the moment.

I’m a firm believer that when you only focus on one thing, you tend to only see things through one perspective. In 2010 I’m going to explore more outside my comfort zone and what I usually work with. Some of the things that I’m exploring at the moment: The Django framework, PHP (building a time tracking application for my freelance business), I just received a few books that I’m reading, and I’m going to explore more of Umbraco (hopefully getting certified in Umbraco this spring). I think all of this will help me become a better developer, and help me with my daily work. I will of course try to share my experiences with you on this blog.

The Create an EPiServer site from scratch was a huge success, with lots of great feedback. Very inspiring for me, giving me lots of ideas for further series. Thanks for all the comments, and keep them coming!

With the EPiServer meetup group in Oslo I hope to meet more of the rest of the community in Norway. One of the things I’ve missed is a community here in Oslo. I think I actually know more EPiServer developers in Sweden than in Norway.. the traffic to this blog actually back that up, 4 times as many from Sweden than from Norway. Hopefully this will change with the meetup group, allowing me to meet all the cool EPiServer developers that I know are out there :) .

Another thing this year is redesigning of my blog. I’m not a designer, so this will be a fun and new experience, again outside my comfort zone, but something I think I’ll learn a lot from. I have a few Photoshop DVD courses that I’m going to go through, learning Photoshop even more, helping me with the redesign and with manipulating the pictures I use in my blog posts.

Since I’m my own boss I’m using Fridays as a “do what you want day”, either taking a long weekend and going somewhere, blogging, redesigning, or any of my other projects. I think this will help me focus and work even better the rest of the week.

Lots of projects and ideas! Very excited to get started and learn even more this year than the last! Thanks for all your support and great comments last year! I’ll do my best to create an even better blog this year!

© Copyright Frederik Vig. Based on Fluid Blue theme