Wednesday, August 25, 2010

AGLS Metadata Support in EPiServer CMS

In almost every tender, RFQ, RFI, or whatever you want to call it, that goes out in Australia there is a mandatory request to support the AGLS Metadata Standard. In governmental type of tenders there are no exception to this rule. To best describe what AGLS Metadata is I’m going to quote the AGLS Metadata web site;

“The AGLS Metadata Standard is a set of descriptive properties to improve visibility and availability of online resources.”

It is a standard used to add metadata to almost any type of resource, but in my case – since I’m working with EPiServer CMS – I’m going to apply it to web pages and also talk about it from this perspective. There are a number of ways to add custom metadata tags to EPiServer pages, and this is also what I tend to say to partners responsible for replying to the already mentioned tenders, RFQs, RFIs… But since the AGLS Metadata support is so common I decided to create an out-of-the-box solution that will add the AGLS Metadata Standard support to EPiServer.

In my work I have used the AGLS Metadata Standard Part 2, Usage Guide (PDF) as reference on how to implement AGLS on web sites. This document is recently updated (mid August 2010) and if you have a look around at random governmental web sites in Australia you will find that most of them still use the legacy implementation mentioned in the document. However since I’m doing a fresh implementation I will go for the new way of adding the metadata tags.

From the Usage Guide you will find that AGLS Metadata is an application profile of Dublin Core metadata standard. This is almost the same as saying that AGLS is a superset of Dublin Core. With this said I decided to make a component that supports both Dublin Core and AGLS, you can simply choose which of these two metadata standards you want to use on your web site.

Dublin Core support for EPiServer CMS has been built before. I think Ben’s implementation is pretty cool, however I decided to take a slightly different approach since I wanted to give the editors an easy way of overriding the default values. To solve this I decided to make use of PageTypeBuilder and create two page base classes; one for Dublin Core and one for AGLS. These base classes will add properties to your PageType corresponding to the elements in Dublin Core and AGLS. All the properties will show up on a Metadata tab on the page with a few exceptions, since some of the properties are always automatically added. The automatic properties are;

  • DCTERMS.created – The creation date of the page
  • DCTERMS.modified – The modified date of the page
  • DCTERMS.format – Will be “text/html”
  • DCTERMS.identifier – Url to the page
  • DCTERMS.language – Language branch of the page
  • AGLSTERMS.availability – Url to the page

To add support for either of the metadata standards you simply inherit from either EPiXternal.Metadata.PageTypes.DublinCore or EPiXternal.Metadata.PageTypes.AGLS – as you have understood by now you have to be using PageTypeBuilder in your project. Then you just have to add the MetadataIncluder web control to the head tag of your MasterPage. This control will look at the current page type and include metadata elements if the page is of type DublinCore or AGLS.

<EPiXternal:MetaData ID="MetaData" runat="server" />

This is how it would like if you view the source of a page implementing AGLS.

<link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" /> 
<link rel="schema.AGLSTERMS" href="http://www.agls.gov.au/agls/terms/" /> 
<meta name="DCTERMS.title" content="Sample Article" /> 
<meta name="DCTERMS.description" content="This is a sample article using the AGLS plugin" /> 
<meta name="DCTERMS.created" scheme="DCTERMS.ISO8601" content="2010-08-19T21:24+10:00" /> 
<meta name="DCTERMS.modified" scheme="DCTERMS.ISO8601" content="2010-08-19T21:24+10:00" /> 
<meta name="DCTERMS.format" scheme="DCTERMS.IMT" content="text/html" /> 
<meta name="DCTERMS.language" scheme="DCTERMS.RFC4646" content="en" /> 
<meta name="DCTERMS.publisher" content="EPiServer Australia Pty Ltd" /> 
<meta name="DCTERMS.creator" content="tom" /> 
<meta name="DCTERMS.coverage" content="World" /> 
<meta name="DCTERMS.rights" scheme="DCTERMS.URI" content="http://pagetypebuilder.local/Copyright/" /> 
<meta name="DCTERMS.identifier" scheme="DCTERMS.URI" content="http://pagetypebuilder.local/Sample-Article/" /> 
<meta name="DCTERMS.type" scheme="DCMIType" content="episerver sample code" /> 
<meta name="DCTERMS.subject" content="episerver sample code" /> 
<meta name="AGLSTERMS.availability" scheme="DCTERMS.URI" content="http://pagetypebuilder.local/Sample-Article/" /> 
<meta name="AGLSTERMS.function" content="education" /> 
<meta name="AGLSTERMS.aggregationLevel" content="collection" /> 
<meta name="AGLSTERMS.category" content="service" /> 
<meta name="AGLSTERMS.documentType" content="guidelines" /> 
<meta name="AGLSTERMS.serviceType" content="training" />

Almost all of the properties will have default values if the properties are not set on the specific page. The default value of each of the properties in edit mode is stated in the help text. The editor also have the possibility to override the defaults by specifying a fallback value on the corresponding properties on the start page. If for some reason you have a fallback value on the start page, but you don’t want it to be displayed for a specific property on a specific page you can just insert a “-“ which will cause the MetadataIncluder to omit that property on the page.

agls-metadata-edit-mode

I hope this will make all of our partners in Australia, and maybe some in UK as well since Dublin Core is big over there. The source code can be downloaded here – enjoy!

Disclaimer: Please not that the code comes as-is and I will not be held responsible.

Friday, August 20, 2010

Multilevel Drop Down Menu in EPiServer CMS

Today I want to share a simple and small code sample that I put together for a partner. In this sample I going to show how you could implement a Drop Down Menu in EPiServer CMS.

There are heaps of Drop Down Menus available on the Internet. I did quick Google search and found this one, which I kind of liked. It is a simple drop down menu with multilevel support built with CSS. You also have the option of adding some JQuery to make it look even prettier. Kudos to Kriesi for building the menu! What I have done is implementing the menu in EPiServer CMS. To learn more about the specific of the CSS and the JavaScript code please visit the original blog post.

To illustrate how you could implement the menu in EPiServer I created a plain PageType. I decided to use the EPiServer PageTree class to build my menu, the code looks like this:

<EPiServer:PageTree runat="server" ID="DropDownMenu" EvaluateHasChildren="true" ExpandAll="true" NumberOfLevels="3">
    <ItemTemplate>
        <li>
            <EPiServer:Property ID="Property1" PropertyName="PageLink" runat="server" /><%# GetItemSeparator(Container.HasChildren) %>
    </ItemTemplate>
    <IndentTemplate>
        <%# GetIndentTemplate(Container.Indent) %>
    </IndentTemplate>
    <UnindentTemplate>
        </ul> 
        </li>
    </UnindentTemplate>
</EPiServer:PageTree>

There are two tricks used in this implementation. Since the menu relies on nested lists, we have to have a dynamic approach to which IndentTemplate and which ItemTemplate to use. For this I created two methods; GetIndentTemplate(int) will add an id tag to the <ul> element if it is the most outer, i.e. the first, list. GetItemSeparator(bool) will make sure we get a nested list if the current item has children. This is how the code looks like:

protected string GetIndentTemplate(int indent)
{
    if (indent == 1)
    {
        return "<ul id=\"nav\">";
    }

    return "<ul>";
}

protected string GetItemSeparator(bool hasChildren)
{
    if (!hasChildren)
    {
        return "</li>";
    }

    return String.Empty;        
}

Using the default CSS that comes with the menu will make your EPiServer CMS page look something like this:

DropDownMenuSample

If you want the complete sample code, you can download it here.

Update!

As Johan mentioned in one of the comments you can make use of the ItemHeaderTemplate and the ItemFooterTemplate to make an alternate implementation of the menu without logic in the code behind. Inspired by the comment by Johan I made some changes to show you how it could look like. Thanks Johan!

<EPiServer:PageTree runat="server" ID="DropDownMenu" EvaluateHasChildren="true" ExpandAll="true" NumberOfLevels="3">        
    <IndentTemplate>
        <%# Container.Indent == 1 ? "<ul id=\"nav\">" : "<ul>" %>
    </IndentTemplate>        
    <ItemHeaderTemplate>
        <li>
    </ItemHeaderTemplate>
    <ItemTemplate>
        <EPiServer:Property ID="epiPageLink" PropertyName="PageLink" runat="server" />
    </ItemTemplate>
    <ItemFooterTemplate>
        </li>
    </ItemFooterTemplate>
    <UnindentTemplate>
        </ul>
    </UnindentTemplate>
</EPiServer:PageTree>

And this version of the code can be downloaded here.

Disclaimer: Please not that the code comes as-is and I will not be held responsible.

Friday, August 6, 2010

Active Directory Membership and Role Providers in Relate+

I don’t get a huge of amount of traffic to my blog, but my most popular posts are the ones about Membership and Role Providers and their configuration in Relate+ found here and here. Today I decided to follow up these post with one covering Relate+ in combination with the Active Directory Membership- and Role Providers. This post is based on information sent to me by my colleague HÃ¥kan and is published with his consent. Thanks Hawk!

The solution to make Active Directory play nice with Relate+ (and vice versa) is in the form of some custom code overriding some things in the Active Directory Providers. The AD Membership and Role Providers are known to cause some issues, something that Johan Olofsson fortunately has blogged about earlier. Start out with the code provided at:

http://labs.episerver.com/en/Blogs/Johano/Dates/2008/9/Some-ActiveDirectoryRoleProvider-issues/

Use that as a base (the code overrides some things in the Active Directory Providers to strip domain names from usernames as well as fixing some issues with escaping of characters in role names), and then, in the WinActiveDirectoryMembershipProvider.cs found there replace the GetUser method with something along these lines:

public override MembershipUser GetUser(string username, bool userIsOnline) 
{ 
  string domain = ""; 
  int bsIndex = username.IndexOf('\\'); 
 
  if(bsIndex > 0) 
  { 
    domain = username.Substring(0, bsIndex + 1); 
    username = username.Substring(bsIndex + 1); 
  } 
 
  MembershipUser user = base.GetUser(username, userIsOnline); 
 
  return new MembershipUser(user.ProviderName, domain + user.UserName, user.ProviderUserKey, user.Email, user.PasswordQuestion, user.Comment, user.IsApproved, user.IsLockedOut, user.CreationDate, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue); 
}

Now set up your site to use this modified WinActiveDirectoryMembershipProvider as your default Membership Provider, the special version of the ActiveDirectoryRoleProvider (from the blog post) as the default Role Provider, and make sure that the “EPiServer.Common.Web.Authorization.IntegrationHttpModule, EPiServer.Common.Web.Authorization” http module is active. And voila – it should work!

What it does different is that it strips the domain from the username in the call to the AD Membership Provider, but then puts the domain back in the MembershipUser it returns to the system. This way the usernames in Membership.GetUser().UserName and HttpContext.Current.User.Identity.Name will match, which is currently required for the Integration http module to work.

The solution is, unfortunately, not well tested, but it does work in my test environment. Happy coding!

Note! This post is primarily written for Relate+ version 1.x