Quantcast
Channel: Joel Abrahamsson
Viewing all 78 articles
Browse latest View live

Lowercase EPiServer URL segments

$
0
0

Make URLs for EPiServer CMS pages lowercased using a simple initialization module.

By default EPiServer CMS generates URL segments, or routing segments, based on a page’s name. It does this by stripping out special characters and replacing whitespaces with hyphens. It does not however make the URL segments lowercased.

Lars Timenes at Epinova has previously written about an easy way to lowercase URLs in EPiServer here. That works great for EPiServer CMS 6. For EPiServer 7 a couple of things has changed though.

Here’ an initialization module for lowercasing URLs for pages when they are created that works for EPiServer 7.

using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.Web;

namespace MySite.Initialization
{
    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class UrlSegmentHandlingInitialization : IInitializableModule
    {
        public void Initialize(InitializationEngine context)
        {
            UrlSegment.CreatedUrlSegment += UrlSegmentOnCreatedUrlSegment;
        }

        private void UrlSegmentOnCreatedUrlSegment(
            object sender, UrlSegmentEventArgs urlSegmentEventArgs)
        {
            var segment = urlSegmentEventArgs.RoutingSegment.RouteSegment
                               .ToLowerInvariant();
            urlSegmentEventArgs.RoutingSegment.RouteSegment = segment;
        }

        public void Uninitialize(InitializationEngine context)
        {
            UrlSegment.CreatedUrlSegment -= UrlSegmentOnCreatedUrlSegment;
        }

        public void Preload(string[] parameters)
        {
        }
    }
}

Beyond lowercasing the URL segment we can of course also modify it in other ways should we want to.

For instance, if a page’s name contains a hyphen surrounded by whitespaces it will by default have a URL segment with three hyphens in a row (“---“). This can easily be fixed in the initialization module with code such as this:

while (segment.Contains("--"))
{
    segment = segment.Replace("--", "-");
}

Lowercase existing URLs

In Lars post he shows how to update URLs for existing pages by modifying their URL segments in the database. That’s probably a preferable approach for sites with a lot of content with EPiServer 7 as well. For smaller sites however it could also be done through the API.

var contentRepository = ServiceLocator.Current
    .GetInstance<IContentRepository>();

var allPages = contentRepository
    .GetDescendents(ContentReference.RootPage)
    .Select(contentRepository.Get<IContent>)
    .OfType<PageData>();

foreach (var page in allPages)
{
    var clone = page.CreateWritableClone();
    var segment = clone.URLSegment.ToLowerInvariant();
    clone.URLSegment = segment;

    contentRepository.Save(
        clone, 
        SaveAction.Publish | SaveAction.ForceCurrentVersion, 
        AccessLevel.NoAccess);
}

Find greatness with EPiServer Find

$
0
0

So far this year I’ve worked with three EPiServer 7 sites. They all use EPiServer Find. Here’s why.

EPiServer Find has gained quite a lot of adoption lately. A number of EPiServer’s partners and customers have realized the great value Find can add to any EPiServer project.

For instance, fellow EMVP Ted Nyberg wrote this on Twitter a while ago:

Three #EPiServer projects now running Find - how did we ever manage without it?

Sometimes though, I meet partner developers who haven’t had the chance to try out Find yet. For those it’s not really clear what Find is, or rather why it’s something quite different compared to the “traditional” search solutions that we are used to working with.

Find by example

So far this year I’ve worked on three different EPiServer 7 sites. On all of them EPiServer Find is an important component. What’s interesting is that while some of them use Find for free text search, free text search functionality hasn’t been the primary reason for using Find for any of them.

Case one – newsroom

The first site is a classic corporate web site. An important part of the site is its newsroom in which content from a number of external sources is aggregated. Within the newsroom the imported content should be easily sortable, filterable and searchable.

The newsroom content is also used on other parts of the site. Different newsroom content should be listed and used depending on the context.

While some of this functionality could be achieved without Find, using Find made it really simple, and fun, to build. To some extent we could have achieved the same result using FindPagesWithCriteria, but that would have brought performance problems and taken longer to build. We would also have had to cut some functionality that FPWC simply couldn’t have handled.

Case two – freeing the taxonomy from the page tree hierarchy

The second site is a new version of this, my own, site. There I use Find to enable a different and more loosely coupled content structure than the native tree structure that EPiServer CMS offers.

On a classic EPiServer site content is organized in a hierarchical structure, the page tree. This structure is almost always used in navigations, breadcrumbs and site maps.

In this case however organizing content in a tree structure isn’t optimal. Content should be divided into categories rather than tree nodes and content should be easily cross-published among various places on the site.

listing_with_find

The CMS’ API is optimized for finding content based on the page tree making such site structures hard to achieve. Especially if there’s a lot of content and performance is important.

By using Find I can easily decouple content from the page tree without worrying about performance implications and can instead focus on building a good taxonomy on the site.

Case three – enabling editorial work flows

The last case is a media site with a huge amount of content. Here too content need to be decoupled from the page tree which, due to the content volume, would have been impossible without Find or something similar.

In this case Find also plays another vital role – enabling editors to work. The page tree and block folders, which are at the core of EPiServer CMS’ edit mode, doesn’t fit the needs of editors on news/media sites.

Instead of working in the site’s structure, editors work with content that is, or is about to be, published during the day – during the current news cycle. Luckily the CMS’ edit mode is highly extensible making it possible to add such functionality to EPiServer.

There’s just one problem. Those UI components need to get their data from the server. Fast. The CMS’ API which is optimized for content organized in the page tree isn’t very good for such non-hierarchical listings.

Find on the other hand is great at finding and sorting content no matter where it’s located on the site, making it the perfect backend for such components.

Beyond the search page

Most traditional search engine vendors talk a lot about “the search page”. That’s natural as their products are very much geared towards free text search functionality.

Although their search engine index’s certainly could be used for other things, the way content is indexed using a crawler and the way their client APIs is designed that would impose time delays. Not to mention tedious work for developers.

One unfortunate consequence of the extreme focus on “the search page” is that the word “search” has been more or less synonymous with free text search in the minds of us developers, not to mention our customers.

Find can certainly be, and is by a growing number, used for free text search with great results. However, as illustrated by the three cases above, search is so much more. And here, in “content retrieval” or querying, is where Find’s true strength lies.

With EPiServer Find in an EPiServer project we not only have an easy way to build free text search but a whole new way of querying for content.

Find essentially duplicates the content objects stored in the CMS’s SQL Server database into a document database. By doing so Finds adds another dimension to developing websites. This new set of tools perfectly compliments the hierarchical view of the content that the CMS’ API is great at.

find_code_example

Conclusion

A search engine shouldn’t just be able to handle free text search.

It should, by utilizing the power of an inverted index, enable us to find and display content in a wide range of scenarios. It should free us as developers from tedious tasks and performance problems.

It should allow us to focus on building great things for our customers and users.

That’s what Find was built for. That’s why I use Find in all of my projects.

Enum properties with EPiServer

$
0
0

Make editors and developers alike happy with properties of custom enum types in EPiServer 7!

As developers we often need some sort of decision from editors where they should be able to choose from a limited number of options. There are ways of accomplishing that out-of-the-box by using drop down list properties or categories. None of them are very developer friendly though.

What I’d like is to create an enum and let editors choose from one of its values.

For instance, imagine that we have the two following enums:

public enum Decision
{
    Automatic = 0,
    No = 1,
    Yes = 2
}

public enum Priority
{
    VeryLow = 1,
    Low = 2,
    Medium = 0,
    High = 3,
    VeryHigh = 4
}

Wouldn’t it be nice if we could easily add properties of those types and let editors work with them using drop downs? Like this:

enums_as_dropdowns

Luckily we can.

There are two magic ingredients – a selection factory and an editor descriptor.

The selection factory

Selection factories are classes implementing ISelectionFactory. They can be used as a sort of data source for properties that are edited using EPiServer’s SelectionEditor.

To avoid having to create separate selection factories for each enum type we can create a generic one:

public class EnumSelectionFactory<TEnum> : ISelectionFactory
{
    public IEnumerable<ISelectItem> GetSelections(
        ExtendedMetadata metadata)
    {
        var values = Enum.GetValues(typeof (TEnum));
        foreach (var value in values)
        {
            yield return new SelectItem
                {
                    Text = GetValueName(value),
                    Value = value
                };
        }
    }

    private string GetValueName(object value)
    {
        var staticName = Enum.GetName(typeof (TEnum), value);

        string localizationPath = string.Format(
            "/property/enum/{0}/{1}",
            typeof (TEnum).Name.ToLowerInvariant(),
            staticName.ToLowerInvariant());

        string localizedName;
        if (LocalizationService.Current.TryGetString(
            localizationPath, 
            out localizedName))
        {
            return localizedName;
        }

        return staticName;
    }
}

Yes, you’re allowed to just copy and paste that code.

The editor descriptor

To allow editors to work with properties of enum types using drop downs we’ll an editor descriptor as the magic clue that connects each property to the selection factory.

public class EnumEditorDescriptor<TEnum> : EditorDescriptor
{
    public override void ModifyMetadata(
        ExtendedMetadata metadata, 
        IEnumerable<Attribute> attributes)
    {
        SelectionFactoryType = typeof(EnumSelectionFactory<TEnum>);

        ClientEditingClass = 
            "epi.cms.contentediting.editors.SelectionEditor";

        base.ModifyMetadata(metadata, attributes);
    }
}

Creating properties

Now we can create properties in page and block types as enums. There’s two things we’ll need to add to each property though.

We need to tell EPiServer how to store the property’s value by mapping it to a type that it can deal with. We do that using a BackingType attribute.

Second, we need to hook up our properties to the editor descriptor with the enum type as type argument.

[BackingType(typeof(PropertyNumber))]
[EditorDescriptor(EditorDescriptorType = typeof(EnumEditorDescriptor<Decision>))]
public virtual Decision ShowTableOfContents { get; set; }

[BackingType(typeof(PropertyNumber))]
[EditorDescriptor(EditorDescriptorType = typeof(EnumEditorDescriptor<Priority>))]
public virtual Priority SearchPriority { get; set; }

That’s a bit more attributes than I’d like. Still, I think it’s a low price to pay considering the alternatives.

Localization

If you paid attention to the code for the selection factory you probably saw that we can localize the select items as well.

<property><enum><decision><automatic>Automagiskt</automatic><yes>Ja!</yes><no>Näe</no></decision></enum></property>

localized_enum_property

EPiServer 7 – How to check if the page is in edit mode

$
0
0
Need to know if an EPiServer CMS page is being rendered in edit mode or not? Then the PageEditing class is your friend.

After trying a number of ways to determine if a page or block is rendered within EPiServer CMS’ edit mode or not I stumbled upon a nice little helper class in the EPiServer API named PageEditing. Using this we can easily check if the current request is for a content object in edit mode in templates and preview pages:

//using EPiServer.Editor;

bool inEditMode = PageEditing.PageIsInEditMode;

I tried this for both pages and block in Web Forms as well as with MVC and it seems to work everywhere.

The PageEditing class also has a couple of other nice members, such as the method GetEditUrl for retrieving the URL for editing a content object  and GetChannel which returns the current channel, if any.

EPiServer 7 and MVC – Custom tags and CSS classes when rendering properties

$
0
0
The PropertyFor HTML helper handles a number of optional parameters for specifying tags and CSS classes. As they are specified using an anonymous object Intellisense isn't much help. Here's a reference of the options available.

The standard way of rendering properties when building a site using EPiServer 7 and ASP.NET MVC is the PropertyFor HTML helper extension method. The Web Forms equivalent is the EPiServer:Property control which can contain a nested RenderSettings control with which we can control some aspects of how the property is rendered. When using the PropertyFor method it’s possible to do the same by passing an object with one or several of those settings as properties. However, the names of these settings aren’t obvious and when using PropertyFor IntelliSense doesn’t help as they are passed as members of an anonymous object.

Therefore, for my own sanity's sense, here’s a quick reference. Please let me know if I’ve missed any setting!

Quick reference

  • CustomTag
  • CssClass
  • ChildrenCustomTagName
  • ChildrenCssClass
  • EditContainerClass

Custom tag

Property name: CustomTag. Example:

@Html.PropertyFor(x => x.PageName, new { CustomTag = "span"})

Note that the CustomTag primarily applies to edit mode when using MVC, with some notable exceptions such as content areas. For instance for a string property no tag is rendered when a page is displayed in view mode. For such types EPiServer will however add a wrapper for the text when displaying it in edit mode and the CustomTag can be used to control what type of element EPiServer wraps it in.

For some property types, such as LinkItemCollection, almost no settings have any effect when using the default rendering. It’s however possible to work around that using custom display templates.

Custom CSS class

Property name: CssClass. Example:

@Html.PropertyFor(x => x.Teasers, new { CssClass = "row" })

Passing a custom CSS class only affects those properties for which a HTML element is created when using PropertyFor, such as content areas. In other words, in the below example the CssClass setting won’t actually do anything at all as no element is created when rendering string properties.

//The CSS class won't be added as no element is created
@Html.PropertyFor(x => x.PageName, new { CssClass = "big" })

Customizing wrapping elements for blocks in content areas

When rendering content area properties a wrapping element will be inserted for each block or other type of content in the content area. The tag can be customized using ChildrenCustomTagName. It’s also possible to add CSS classes to these elements using ChildrenCssClass. Example:

@Html.PropertyFor(x => x.Teasers, 
    new
        {
            ChildrenCustomTagName ="span", 
            ChildrenCssClass = "block"
        })

Customizing wrapping elements in edit mode

When properties are rendered in edit mode EPiServer inserts a wrapping element around the property’s value. By default this wrapping element is a div with a CSS class named epi-editContainer which forces the element to have a minimum width and height. It’s possible to change that CSS class to a custom class using EditContainerClass. By utilizing this we can customize the size and other characteristics of the wrapping element which I’ve personally found very useful in some specific cases. Example:

@Html.PropertyFor(x => x.PageName, 
    new { EditContainerClass = "inline" })

Building a PDF Channel for EPiServer 7

$
0
0
Channels in EPiServer 7 is an exciting new feature that can be used for a variety of purposes. While it may not be the first use case that comes to mind we can use channels to enable rendering of pages on a site as PDF documents.

Last Thursday at the Stockholm EPiServer Meetup I demoed a quick and easy way to create a PDF channel for an EPiServer 7 site using the open source library Rotativa. While it may not be sexiest use case for channels it may actually be useful for some sites. Not to mention that it’s fun to be able to surf around on the site in PDF format while in edit mode. Here’s how to build it.

The channel

Channels in EPiServer 7 couldn’t be much easier to define. We simply create a class inheriting from DisplayChannel and implement two abstract methods.

//using System.Web;
//using EPiServer.Web;

public class PdfChannel : DisplayChannel
{
    public override bool IsActive(HttpContextBase context)
    {
        return context.Request.QueryString["pdf"] == 1.ToString();
    }

    public override string ChannelName
    {
        get { return "PDF"; }
    }
}

The above class will add a new channel to a site which will be selectable in edit mode. It will also be active if there’s a query string parameter named pdf with a value of 1 in the URL. That is, no matter which page is being requested it will be active if the query string parameter is there.

The controller

Rotativa is a nice little library which wraps the larger wkhtmltopdf project which can be used to generate PDF documents from HTML markup through the use of WebKit. Rotativa is easily downloadable from NuGet.

As Rotativa is built for being used with ASP.NET MVC we’ll add a renderer for the PDF channel in the form of an MVC controller. With EPiServer 7 doing so will work no matter if the rest of the site is built using MVC or Web Forms.

//using System.Web.Mvc;
//using EPiServer.Core;
//using EPiServer.Framework.DataAnnotations;
//using EPiServer.ServiceLocation;
//using EPiServer.Web.Mvc;
//using EPiServer.Web.Routing;
//using Rotativa;

[TemplateDescriptor(Inherited = true, Tags = new [] { "pdf" })]
public class PdfController : PageController<PageData>
{
    public ActionResult Index(PageData currentPage)
    {
        var url = ServiceLocator.Current
            .GetInstance<UrlResolver>()
            .GetVirtualPath(currentPage.ContentLink);

        return new UrlAsPdf(url)
        {
            FormsAuthenticationCookieName = ".EPiServerLogin"
        };
    }
}

The above controller simply receives the current page, figures out its URL in an MVC and Web Forms agnostic way and returns a UrlAsPdf result which is an action result type added by Rotativa. To ensure that the PDF generator will be able to reach the page even in edit mode when using ASP.NET MVC we alse ensure that it can use the correct authentication cookie.

The result – any page as PDF

With the channel and renderer in place we’re now able to select the PDF channel in edit mode to preview any page as a PDF document. Public visitors are also able to get any page as PDF by adding pdf=1 to the query string. Of course, in a real world scenario we’d still have the bulk of work in front of us if we wanted the channel to be useful as we’d probably want to use a separate stylesheet. Nevertheless, here’s how it may look in edit mode.

pdf-channel

Limiting content and page reference properties to values of a specific type in EPiServer CMS

$
0
0
By limiting available options to only valid values for PageReference and ContentReference properties in EPiServer 7 we can improve the user experience for editors. By doing so we can also protect our sites from invalid and potentially harmful editorial settings.

A fairly common scenario when building EPiServer CMS sites is that we as developers add a PageReference property to a page type whose value should be a reference to a page of a specific page type. We may for instance want editors to select a product page, a search page or a person page. Simply adding a PageReference property works but then we can’t be sure that the editor will actually set it to a page of the type we expect. Also, for editors, having to sort through the entire page tree to locate a page of a limited number of pages of that types is tedious.

A filtered page reference property

In previous versions of EPiServer it’s been tricky to achieve a solution that both validated that the selected page was of the correct type and enable editors to only have to choose amongst relevant pages. In the Alloy templates for EPiServer 7 however an elegant solution to this problem is illustrated. The ContactBlock class has a property named ContactPageLink which when rendered in edit mode looks like this:

contactpageselector

As you can see, editors are only able to select pages of a specific type and they are also able to do so by using a drop down with all those pages rather than having to sort through the entire page tree. Me like!

The code for the ContactPageLink property looks like this:

[Display(
    GroupName = SystemTabNames.Content,
    Order = 3)]
[UIHint(Global.SiteUIHints.Contact)]
public virtual PageReference ContactPageLink { get; set; }

The magic ingredient here is the UIHint. Using that the property is mapped to an “editor descriptor” named ContactPageSelector. The code for that looks like this:

[EditorDescriptorRegistration(
    TargetType = typeof(PageReference), 
    UIHint = Global.SiteUIHints.Contact)]
public class ContactPageSelector : EditorDescriptor
{
    public override void ModifyMetadata(
        ExtendedMetadata metadata, 
        IEnumerable<Attribute> attributes)
    {
        SelectionFactoryType = typeof(ContactPageSelectionFactory);
        ClientEditingClass = "epi.cms.contentediting.editors.SelectionEditor";
        base.ModifyMetadata(metadata, attributes);
    }
}

The editor descriptor instructs the edit mode to render the property as a dropdown by setting the ClientEditingClass. It also hooks up the dropdown to a data source, a custom selection factory named ContactPageSelectionFactory. The code for the selection factory looks like this:

public class ContactPageSelectionFactory : ISelectionFactory
{
    public IEnumerable<ISelectItem> GetSelections(
        ExtendedMetadata metadata)
    {
        var contactPages = SiteDataFactory.Instance
            .GetContactPages();

        return new List<SelectItem>(contactPages
            .Select(c => new SelectItem
                {
                    Value = c.PageLink, 
                    Text = c.Name
                }));
    }
}

The selection factory uses more custom code in the Alloy templates to locate all pages of type ContactPage and then creates a SelectItem for each of them. These SelectItem objects are what’s used to populate the dropdown while the selected value is stored as the property’s value.

A generic approach

I really like what we’re achieving using the above code! As a developer I can ensure that editors can only insert valid values into the property. Also, given that the number of pages of the desired type is fairly small, the user experience for editors is greatly improved.

There’s just one problem. If we want to use the same approach for other properties where editors should be able to choose pages, or other types of content such as blocks, of a different type we can’t reuse any of the existing code but instead have to come up with another UI hint string, add another editor descriptor as well as another selection factory.

Instead of having to do all that tedious and error prone work I’d like a generic solution with which all we’d have to do to limit the possible selections to content of a specific type would be to add an attribute. Like this:

[ContentSelection(typeof(ProductPage))]
public virtual PageReference Product { get; set; }

[ContentSelection(typeof(ContactPage))]
public virtual PageReference ContactPage { get; set; }

[ContentSelection(typeof(TeaserBlock))]
public virtual ContentReference Teaser { get; set; }

Looks pretty nice, right? Let’s do it!

Solution

First of all we need an attribute which could only be simpler if attributes could have generic type parameters.

[AttributeUsage(
    AttributeTargets.Property, 
    AllowMultiple = false)]
public class ContentSelectionAttribute : Attribute
{
    public ContentSelectionAttribute(Type contentType)
    {
        ContentType = contentType;
    }

    public Type ContentType { get; set; }
}

The editor descriptor

With the attribute in place we can add it to properties but it won’t yet have any effect. To make the attribute matter we’ll need to create an editor descriptor.

//using System;
//using System.Collections.Generic;
//using System.Linq;
//using EPiServer.Core;
//using EPiServer.Shell.ObjectEditing;
//using EPiServer.Shell.ObjectEditing.EditorDescriptors;

[EditorDescriptorRegistration(
    TargetType = typeof(ContentReference))]
[EditorDescriptorRegistration(
    TargetType = typeof(PageReference))]
public class ContentSelector : EditorDescriptor
{
    public override void ModifyMetadata(
        ExtendedMetadata metadata, 
        IEnumerable<Attribute> attributes)
    {
        var contentSelectionAttribute = metadata.Attributes
            .OfType<ContentSelectionAttribute>()
            .SingleOrDefault();

        if(contentSelectionAttribute != null)
        {
            SelectionFactoryType =  
                typeof (ContentSelectionFactory<>)
                    .MakeGenericType(
                        contentSelectionAttribute.ContentType);

            ClientEditingClass = 
                "epi.cms.contentediting.editors.SelectionEditor";
        }

        base.ModifyMetadata(metadata, attributes);
    }
}

As opposed to the Alloy templates we register our editor descriptor for both PageReference and ContentReference and omit the UIHint. This means that it will be used for all properties of type PageReference as well as ContentReference.

In the ModifyMetadata method we check if the given property is annotated with our ContentSelection attribute. If it is we hook it up to a custom selection factory with the type specified in the attribute as type argument.

A generic selection factory

Speaking of the custom selection factory, that’s our next and final step.

//using System.Collections.Generic;
//using System.Linq;
//using EPiServer.Core;
//using EPiServer.DataAbstraction;
//using EPiServer.ServiceLocation;
//using EPiServer.Shell.ObjectEditing;

public class ContentSelectionFactory<T> : ISelectionFactory
    where T : IContentData
{
    private Injected<IContentTypeRepository> 
        ContentTypeRepository { get; set; }
    private Injected<IContentModelUsage> 
        ContentModelUsage { get; set; }
    private Injected<IContentLoader> 
        ContentLoader { get; set; }

    public IEnumerable<ISelectItem> GetSelections(
        ExtendedMetadata metadata)
    {
        var contentType = ContentTypeRepository.Service
            .Load<T>();
        if(contentType == null)
        {
            return Enumerable.Empty<SelectItem>();
        }

        var selectItems = 
            ContentModelUsage.Service
            .ListContentOfContentType(contentType)
            .Select(x => 
                x.ContentLink.CreateReferenceWithoutVersion())
            .Distinct()
            .Select(x => ContentLoader.Service.Get<T>(x))
            .OfType<IContent>()
            .Select(x => new SelectItem
                {
                    Text = x.Name,
                    Value = x.ContentLink
                })
            .OrderBy(x => x.Text)
            .ToList();
        selectItems.Insert(0, new SelectItem());
        return selectItems;
    }
}

The above code is quite the mouth full. Sorry about that. Contrary to it’s appearance however the essence of what it does is straight forward – it returns a SelectItem for each page on the site of the type specified in the type parameter. It also adds a an empty item first in the list before returning it. If it hadn’t properties with the attribute would appear prepopulated when editors create a new page and editors wouldn’t be able to set their values to null (we can always add a Required attribute if they shouldn’t be able to do that.

The desired functionality of the selection factory may vary from site to site. For instance, in a multisite scenario we’d might like to filter out content from other sites. As for the technical implementation there are several ways to achieve the same result but that’s a different blog post.

Result

With the above three classes in place we now have a generic way of creating filtered page and content reference properties whose selectable values are limited to a specific content type. As an example, the code I wanted to be able to write…

[ContentSelection(typeof(ProductPage))]
public virtual PageReference Product { get; set; }

[ContentSelection(typeof(ContactPage))]
public virtual PageReference ContactPage { get; set; }

[ContentSelection(typeof(TeaserBlock))]
public virtual ContentReference Teaser { get; set; }

… gives us this in edit mode …

multiple-content-selectors

multiple-content-selectors-expanded-1

multiple-content-selectors-expanded-2

EPiServer Find Training

$
0
0
Come and learn how to do content retrieval and great free text search using EPiServer Find.

I occasionally work as a technical trainer for EPiServer Training. I enjoy that as I get to meet people from different companies as well as people from different parts of the country, and sometimes the world. As one may also guess given my background with 200OK and Truffler, the product now known as EPiServer Find, I feel strongly about EPiServers coolest product, Find ;-)

EpiServerLogo_FINDTherefore I’m really happy to announce that I’ll be giving a course in EPiServer Find starting with a first course date in Stockholm set to February 1st and another one scheduled for March 8th. So, if you work with EPiServer products, or if you’re curious about the possibilities that an inverted index offers both in terms of performance and functionality, and want to learn how to build awesome things with Find, be sure to check it out!

End shameless promotional plug.


Upgrading a site from EPiServer CMS 6 to EPiServer 7

$
0
0
EPiServer 7 brings a lot of changes and a new editorial UI. Meanwhile the API is said to be backwards compatible. I've investigated the upgrade experience and found a few interesting things.

I’ve been investigating the upgrade experience from EPiServer CMS version 6 R2 to version 7 lately. Those that have ever upgraded an EPiServer 4 site to version 5 probably know that that’s no easy feat. In fact, I typically recommend my customers to build a new site rather than upgrading. Upgrading from version 5 to 6 on the other hand wasn’t a big deal.

EPiServer 7 features a totally new edit mode and quite a lot of API changes. On the other hand the developers at EPiServer have worked hard to maintain backwards compatibility in a way that should enable us to gradually start using new API features rather than having to rewrite everything just to get an upgraded site to work.

Also, while EPiServer 7 requires .NET 4 instead of .NET 3.5 there hasn’t been any major changes in the underlying platform which EPiServer builds upon, as opposed to the version 4 to 5 upgrade where EPiServer threw out a bunch of custom stuff in favor of standardized components introduced in .NET 2.0 and ASP.NET 2.0.

So, in theory upgrading from 6 R2 to 7 should be a smooth ride. We should expect custom properties to work but not fit into the new design of the edit UI and of course customizations of the old edit UI would have to be replaced. Other than that though, there shouldn’t be any major issues.

Of course we all know that theory and reality rarely meet when dealing with upgrades. Therefor I decided to get my hands dirty and do some upgrading for real. As a first step I decided to set up the standard sample site for EPiServer 6, AlloyTech, and upgrade that to EPiServer 7. Here’s how it went.

Deployment Center and .NET 4

Upgrading through EPiServer’s Deployment Center proved to be a simple next-next-next experience. We simply have to select “Upgrade site with SQL Server database” in the All Actions tab and then select a site to upgrade.

DeploymentCenter

Although I’m sure some will run in to edge cases where there will be problems performing the upgrade I had no problems at all. The only warning that I got was that the “project will have to be compiled after upgrading” which I guess was actually more of a friendly reminder than an actual warning.

After having done the upgrade with Deployment Center I found that it had created a new application pool running .NET 4 in IIS and changed the site’s configuration to use that. Proceeding to open up the project in Visual Studio I ran into a funny little oddity.

Before I even got to open the project though, I was faced with a prompt where I had to choose whether to change the site back to .NET 2.0 or not as the project was still targeting .NET 3.5.

Dialog

Of course I didn’t want that so I clicked no.

Now, remember that EPiServer 7 requires .NET 4 and that Deployment Center had told me I needed to recompile the project. The old Alloy templates are by default configured to target .NET 3.5 and neither myself nor Deployment Center’s upgrade had changed that. Deployment Center had however changed the EPiServer references to their EPiServer 7 versions. Clearly the project shouldn’t compile as it was still targeting an older .NET framework than the referenced EPiServer assemblies.

It did however compile! Of course it didn’t actually, but since I hadn’t changed a single source file Visual Studio didn’t think it actually needed to do anything and reported a successful compilation. Of course, when browsing the site I was met with an angry yellow screen of death, saying “Method not found: 'Boolean EPiServer.Core.PageReference.CompareToIgnoreWorkID(EPiServer.Core.PageReference)'.”.

So, I went ahead and changed the Target Framework to .NET 4.

Fiftyseven compilation errors

Changing target framework made Visual Studio recognize that it actually needed to do some work when I asked it to compile the next time. Now, however, when one could almost expect a successful compilation I was faced with 57 angry errors.

CompilationErrors

Didn’t I say something about EPiServer’s developer having worked hard to maintain a level of backward compatibility with which we should be able to adjust to API changes gradually? As it turned out a lot of the compilation errors were actually warnings about using deprecated methods. Changing the project to not treat warnings as errors brought the compilation error list down to 14 errors.

WarningsAsErrors

The remaining 14 “real” errors could be grouped into three categories.

First of all there were a number of errors due to EPiServer having marked types and methods as obsolete in a way that makes the compiler treats usages of them as errors. In most of those cases fixing the error was easy as pie as EPiServer had also provided a good workaround suggestion in the obsolete attributes.

In one case it wasn’t quite as easy though. As EPiServer 7 supports “any content” in the form of IContent rather than just pages in the form of PageData objects a lot of methods and properties that used to work with PageReferences now expect or return ContentReferences instead. In the AlloyTech code there is a method that looks like this:

SoftLink softLink = (SoftLink)link;
return String.Format("{0} [{1}]", 
    DataFactory.Instance.GetPage(softLink.OwnerPageLink).PageName, 
    softLink.OwnerPageLink.ID);

This little bugger proved slightly more problematic to fix than the other compilation errors due to things having been marked as obsolete. The problem is that SoftLink.PageLink is obsolete and we’re instructed to instead use SoftLink.ContentLink which returns a ContentReference instead of a PageReference. Only, the GetPage method, which I guess one could argue is semi obsolete, requires a PageReference.

Luckily, there’s an easy solution to this. We simply use Get<PageData>() instead of GetPage() as the former is satisfied with a ContentReference as argument. Alternatively we could have constructed a PageReference as the ContentReference has all the required data for doing so, but why bother when we should prefer the new and shiny Get<T>() over the old and dusty GetPage() anyway.

Four compilation errors

Having mostly just followed the instructions from EPiServer, replacing usages of obsolete types and methods with the suggested equivalents I was down to four compilation errors.

Two of those were caused by some code which disabled On Page Edit for a couple of templates. As there isn’t any such thing as (the old) On Page Edit (or DOPE as it’s really called) fixing this simply involved removing the code that didn’t compile.

Finally the last two errors were due to code that instantiated the SiteConfigDB class which no longer has a parameter less constructor and instead requires an IDatabaseHandler as argument. While I can’t say that I’m very familiar with either the SiteConfigDB class or the IDatabaseHandler interface it was quite easy to guess how to fix that – by using the ServiceLocator.

//Before
var siteConfigDB = new SiteConfigDB();

//After
var databaseHandler = ServiceLocator.Current.GetInstance<IDatabaseHandler>();
var siteConfigDB = new SiteConfigDB(databaseHandler);

Build succeeded!

An URL isn't no string no more

Having fixed the last compilation errors I was happy to see that the site’s start page loaded without runtime errors. However, it looked a bit bare as it lacked a logotype. Also, when loading some pages I got a yellow screen of death saying “Unable to cast object of type 'EPiServer.Url' to type 'System.String'”.

In both cases the problem was code that retrieved a URL property’s value from a PageData object using indexer syntax and then casted the value to a string. That worked fine in EPiServer 6 but in 7 the value type for an URL property isn’t a simple string anymore. Instead it’s a Url, as in EPiServer.Url.

So, to get back the logotype and fix runtime errors due to type conversion errors I had to modify such code. For instance, here’s what I did to fix the logotype:

//Before
Logotype.ImageUrl = StartPageData["Logotype"] as string ?? string.Empty;

//After
var logoUrl = StartPageData["Logotype"] as Url;
if (logoUrl != null && !logoUrl.IsEmpty())
{
    Logotype.ImageUrl = logoUrl.ToString();
}

Personally I can’t see any reason why EPiServer couldn’t have added an implicit operator from Url to string which might have made life easier for us. Either way, fixing these issues was straight forward. I could however imagine quite a few projects where developers have written “safe” code that handles when a property’s value isn’t of a specific type. Tracking down bugs due to the changes of value types for properties such as PropertyUrl may prove tiresome in such projects.

Custom properties

According to what I’ve heard and understood custom properties *should* continue to function as before after an upgrade, albeit with custom editing controls displayed in an IFrame. Curious about this, one of the first things I did after successfully upgrading the site was to head over to a Blog item page as I knew that those featured a custom property named BlogTags. Here’s how such a property looked in EPiServer 6’s edit mode:

CustomProperty

Disappointingly I found the property displayed as a regular string in 7’s forms edit mode.

Custom_property_in_7

Analyzing the custom property I realized that it was, given that I’ve worked quite a bit with EPiServer 7, fairly obvious why the custom property control wasn’t used. The old way of connecting a property (as in class inheriting PropertyData or one of its sub types) was to override the CreatePropertyControl method and return an instance of the control that should be used. That’s exactly what was done in this custom property:

public override IPropertyControl CreatePropertyControl()
{
    return new PropertyBlogTagsControl();
}

In EPiServer 7, that doesn’t do anything. Instead I needed to connect the control to the property type in some other way. I did so by adding an EditorHint attribute to both the property class and the control.

[EditorHint("BlogTags")]
public class PropertyBlogTagsControl 
    : Web.PropertyControls.PropertyTextBoxControlBase
//Class definition

[PageDefinitionTypePlugIn]
[EditorHint("BlogTags")]
public class PropertyBlogTags : PropertyString
//Class definition

Et voilà! The property could be edited using a “legacy editor”.

custom_property_with_legacy_editor

Conclusion

After having upgraded the EPiServer 6 version of the Alloy site, twice, I’m surprisingly unsurprised. The experience was pretty much what I had expected as the upgrade did involve fixing some, but not that many, compilation errors. The custom property still worked, albeit after a little adjustment and not exactly looking superb in the context of the sleek new edit UI.

Based on my experiences so far I’d say that upgrading from version 6 to 7 is neither exactly easy nor is it a hellish experience. If one has some experience with EPiServer 7 and the site doesn’t rely on major customizations of the old edit mode I’d account for maybe a days work to perform the initial upgrade of a site.

Reaping the benefits of EPiServer 7’s new edit UI and live preview functionality may however require far more time, much depending on how the site was originally built. If for instance a lot of properties wasn’t rendered using the EPiServer:Property control the site would work but we’d have a fair amount of work in front of us before editors would be able to work outside of forms edit mode.

Likewise, what I’ve described here is the initial steps of the technical upgrade. I still have 43 compiler warnings that should be tended to, configuration files to go through and new coding practices to adopt in the sites code base. Not to mention deployment.

Knowledge about EPiServer 7 is key

I should add that if I hadn’t had worked a lot with EPiServer 7 prior to the upgrade I probably would have had to spend quite a lot more time on the initial upgrade. So, before you think about upgrading anything beyond the simplest of sites, be sure that you have some knowledge and experience with the new version. With that, the basic upgrade shouldn’t be too hard and you can focus on details and making the site nice to work with for editors rather than having to get into a boxing fight with the compiler.

Result

I leave you with this, a screenshot of the good old (well… old at least ;-)) AlloyTech site in EPiServer 7’s new edit UI, Sparrowhawk.

Old_Alloy_in_Sparrohawk

Lowercase EPiServer URL segments

$
0
0
Make URLs for EPiServer CMS pages lowercased using a simple initialization module.

By default EPiServer CMS generates URL segments, or routing segments, based on a page’s name. It does this by stripping out special characters and replacing whitespaces with hyphens. It does not however make the URL segments lowercased.

Lars Timenes at Epinova has previously written about an easy way to lowercase URLs in EPiServer here. That works great for EPiServer CMS 6. For EPiServer 7 a couple of things has changed though.

Here’ an initialization module for lowercasing URLs for pages when they are created that works for EPiServer 7.

using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.Web;

namespace MySite.Initialization
{
    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class UrlSegmentHandlingInitialization : IInitializableModule
    {
        public void Initialize(InitializationEngine context)
        {
            UrlSegment.CreatedUrlSegment += UrlSegmentOnCreatedUrlSegment;
        }

        private void UrlSegmentOnCreatedUrlSegment(
            object sender, UrlSegmentEventArgs urlSegmentEventArgs)
        {
            var segment = urlSegmentEventArgs.RoutingSegment.RouteSegment
                               .ToLowerInvariant();
            urlSegmentEventArgs.RoutingSegment.RouteSegment = segment;
        }

        public void Uninitialize(InitializationEngine context)
        {
            UrlSegment.CreatedUrlSegment -= UrlSegmentOnCreatedUrlSegment;
        }

        public void Preload(string[] parameters)
        {
        }
    }
}

Beyond lowercasing the URL segment we can of course also modify it in other ways should we want to.

For instance, if a page’s name contains a hyphen surrounded by whitespaces it will by default have a URL segment with three hyphens in a row (“---“). This can easily be fixed in the initialization module with code such as this:

while (segment.Contains("--"))
{
    segment = segment.Replace("--", "-");
}

Lowercase existing URLs

In Lars post he shows how to update URLs for existing pages by modifying their URL segments in the database. That’s probably a preferable approach for sites with a lot of content with EPiServer 7 as well. For smaller sites however it could also be done through the API.

var contentRepository = ServiceLocator.Current
    .GetInstance<IContentRepository>();

var allPages = contentRepository
    .GetDescendents(ContentReference.RootPage)
    .Select(contentRepository.Get<IContent>)
    .OfType<PageData>();

foreach (var page in allPages)
{
    var clone = page.CreateWritableClone();
    var segment = clone.URLSegment.ToLowerInvariant();
    clone.URLSegment = segment;

    contentRepository.Save(
        clone, 
        SaveAction.Publish | SaveAction.ForceCurrentVersion, 
        AccessLevel.NoAccess);
}

Find greatness with EPiServer Find

$
0
0
So far this year I’ve worked with three EPiServer 7 sites. They all use EPiServer Find. Here’s why.

EPiServer Find has gained quite a lot of adoption lately. A number of EPiServer’s partners and customers have realized the great value Find can add to any EPiServer project.

For instance, fellow EMVP Ted Nyberg wrote this on Twitter a while ago:

Three #EPiServer projects now running Find - how did we ever manage without it?

Sometimes though, I meet partner developers who haven’t had the chance to try out Find yet. For those it’s not really clear what Find is, or rather why it’s something quite different compared to the “traditional” search solutions that we are used to working with.

Find by example

So far this year I’ve worked on three different EPiServer 7 sites. On all of them EPiServer Find is an important component. What’s interesting is that while some of them use Find for free text search, free text search functionality hasn’t been the primary reason for using Find for any of them.

Case one – newsroom

The first site is a classic corporate web site. An important part of the site is its newsroom in which content from a number of external sources is aggregated. Within the newsroom the imported content should be easily sortable, filterable and searchable.

The newsroom content is also used on other parts of the site. Different newsroom content should be listed and used depending on the context.

While some of this functionality could be achieved without Find, using Find made it really simple, and fun, to build. To some extent we could have achieved the same result using FindPagesWithCriteria, but that would have brought performance problems and taken longer to build. We would also have had to cut some functionality that FPWC simply couldn’t have handled.

Case two – freeing the taxonomy from the page tree hierarchy

The second site is a new version of this, my own, site. There I use Find to enable a different and more loosely coupled content structure than the native tree structure that EPiServer CMS offers.

On a classic EPiServer site content is organized in a hierarchical structure, the page tree. This structure is almost always used in navigations, breadcrumbs and site maps.

In this case however organizing content in a tree structure isn’t optimal. Content should be divided into categories rather than tree nodes and content should be easily cross-published among various places on the site.

listing_with_find

The CMS’ API is optimized for finding content based on the page tree making such site structures hard to achieve. Especially if there’s a lot of content and performance is important.

By using Find I can easily decouple content from the page tree without worrying about performance implications and can instead focus on building a good taxonomy on the site.

Case three – enabling editorial work flows

The last case is a media site with a huge amount of content. Here too content need to be decoupled from the page tree which, due to the content volume, would have been impossible without Find or something similar.

In this case Find also plays another vital role – enabling editors to work. The page tree and block folders, which are at the core of EPiServer CMS’ edit mode, doesn’t fit the needs of editors on news/media sites.

Instead of working in the site’s structure, editors work with content that is, or is about to be, published during the day – during the current news cycle. Luckily the CMS’ edit mode is highly extensible making it possible to add such functionality to EPiServer.

There’s just one problem. Those UI components need to get their data from the server. Fast. The CMS’ API which is optimized for content organized in the page tree isn’t very good for such non-hierarchical listings.

Find on the other hand is great at finding and sorting content no matter where it’s located on the site, making it the perfect backend for such components.

Beyond the search page

Most traditional search engine vendors talk a lot about “the search page”. That’s natural as their products are very much geared towards free text search functionality.

Although their search engine index’s certainly could be used for other things, the way content is indexed using a crawler and the way their client APIs is designed that would impose time delays. Not to mention tedious work for developers.

One unfortunate consequence of the extreme focus on “the search page” is that the word “search” has been more or less synonymous with free text search in the minds of us developers, not to mention our customers.

Find can certainly be, and is by a growing number, used for free text search with great results. However, as illustrated by the three cases above, search is so much more. And here, in “content retrieval” or querying, is where Find’s true strength lies.

With EPiServer Find in an EPiServer project we not only have an easy way to build free text search but a whole new way of querying for content.

Find essentially duplicates the content objects stored in the CMS’s SQL Server database into a document database. By doing so Finds adds another dimension to developing websites. This new set of tools perfectly compliments the hierarchical view of the content that the CMS’ API is great at.

find_code_example

Conclusion

A search engine shouldn’t just be able to handle free text search.

It should, by utilizing the power of an inverted index, enable us to find and display content in a wide range of scenarios. It should free us as developers from tedious tasks and performance problems.

It should allow us to focus on building great things for our customers and users.

That’s what Find was built for. That’s why I use Find in all of my projects.

Enum properties with EPiServer

$
0
0
Make editors and developers alike happy with properties of custom enum types in EPiServer 7!

As developers we often need some sort of decision from editors where they should be able to choose from a limited number of options. There are ways of accomplishing that out-of-the-box by using drop down list properties or categories. None of them are very developer friendly though.

What I’d like is to create an enum and let editors choose from one of its values.

For instance, imagine that we have the two following enums:

public enum Decision
{
    Automatic = 0,
    No = 1,
    Yes = 2
}

public enum Priority
{
    VeryLow = 1,
    Low = 2,
    Medium = 0,
    High = 3,
    VeryHigh = 4
}

Wouldn’t it be nice if we could easily add properties of those types and let editors work with them using drop downs? Like this:

enums_as_dropdowns

Luckily we can.

There are two magic ingredients – a selection factory and an editor descriptor.

The selection factory

Selection factories are classes implementing ISelectionFactory. They can be used as a sort of data source for properties that are edited using EPiServer’s SelectionEditor.

To avoid having to create separate selection factories for each enum type we can create a generic one:

public class EnumSelectionFactory<TEnum> : ISelectionFactory
{
    public IEnumerable<ISelectItem> GetSelections(
        ExtendedMetadata metadata)
    {
        var values = Enum.GetValues(typeof (TEnum));
        foreach (var value in values)
        {
            yield return new SelectItem
                {
                    Text = GetValueName(value),
                    Value = value
                };
        }
    }

    private string GetValueName(object value)
    {
        var staticName = Enum.GetName(typeof (TEnum), value);

        string localizationPath = string.Format(
            "/property/enum/{0}/{1}",
            typeof (TEnum).Name.ToLowerInvariant(),
            staticName.ToLowerInvariant());

        string localizedName;
        if (LocalizationService.Current.TryGetString(
            localizationPath, 
            out localizedName))
        {
            return localizedName;
        }

        return staticName;
    }
}

Yes, you’re allowed to just copy and paste that code.

The editor descriptor

To allow editors to work with properties of enum types using drop downs we’ll an editor descriptor as the magic clue that connects each property to the selection factory.

public class EnumEditorDescriptor<TEnum> : EditorDescriptor
{
    public override void ModifyMetadata(
        ExtendedMetadata metadata, 
        IEnumerable<Attribute> attributes)
    {
        SelectionFactoryType = typeof(EnumSelectionFactory<TEnum>);

        ClientEditingClass = 
            "epi.cms.contentediting.editors.SelectionEditor";

        base.ModifyMetadata(metadata, attributes);
    }
}

Creating properties

Now we can create properties in page and block types as enums. There’s two things we’ll need to add to each property though.

We need to tell EPiServer how to store the property’s value by mapping it to a type that it can deal with. We do that using a BackingType attribute.

Second, we need to hook up our properties to the editor descriptor with the enum type as type argument.

[BackingType(typeof(PropertyNumber))]
[EditorDescriptor(EditorDescriptorType = typeof(EnumEditorDescriptor<Decision>))]
public virtual Decision ShowTableOfContents { get; set; }

[BackingType(typeof(PropertyNumber))]
[EditorDescriptor(EditorDescriptorType = typeof(EnumEditorDescriptor<Priority>))]
public virtual Priority SearchPriority { get; set; }

That’s a bit more attributes than I’d like. Still, I think it’s a low price to pay considering the alternatives.

Localization

If you paid attention to the code for the selection factory you probably saw that we can localize the select items as well.

<property><enum><decision><automatic>Automagiskt</automatic><yes>Ja!</yes><no>Näe</no></decision></enum></property>

localized_enum_property

Tests for the EPiServer 7 MVC templates

$
0
0
Example unit tests for the MVC version of the EPiServer 7 Alloy templates available for download.

During the development of the ASP.NET MVC templates for EPiServer 7 I created a number of tests, or executable specifications, in a separate project. As it may be of interest to others I've made it available for download on EPiServer World.

The tests uses a test framework, or rather a Context/Specification framework, named Machine.Specifications, or MSpec for short. In order to stub out CMS components such as IContentLoader the project also uses the mocking framework FakeItEasy.

The purpose of the tests isn't to provide a hundred percent code coverage for the production code. It does however offer decent coverage of the content area rendering functionality which is probably the most complex part of the Alloy templates, and also one of the parts most likely to be used in "real" projects.

It also features a couple of tests for the search page's controller. Unlike the content area rendering tests, which I found highly useful when building the templates, the purpose of these is primarily to provide examples of how it's possible to test controllers and similar components when using EPiServer CMS.

About MSpec

If you're familiar with traditional test frameworks such as NUnit, xUnit.NET or (god forbid) MS Test the tests may seem a bit odd. That's because MSpec isn't a conventional unit test framework but instead a framework for a concept known as Context/Specification.

With MSpec we create classes whose name should describe a certain context. That context is created by creating a delegate variable of type Establish.

public class given_a_search_query_which_matches_a_single_page : SearchPageControllerSpec
{
    Establish context = () =>
    {
        //Set things up
    };
};

Next we describe a specific action, such as a method call triggered by a user interaction.

Because of = () => result = controller.Index(new SearchPage(), SearchQuery);

Finally we verify that one or several things have happened as a result of the action.

It should_populate_the_view_model_with_a_single_hit = () =>
    ((SearchPageModel)result.Model).Hits.Count().ShouldEqual(1);

The syntax, using variables with long, descriptive names and delegates may seem strange. One reason for it though is that it helps us to be expressive and when running the tests/specifications the output can be used to see the purpose of the test.

For instance, the output from running the above specification looks like this:

given a search query which matches a single page
» should populate the view model with a single hit

Why MSpec?

EPiServer is, at least for the most part, test framework agnostic. Nor does EPiServer, as far as I know, make any specific recommendations regarding test frameworks.

I choose to use MSpec as I've used it a lot before and like it a lot. In fact, I've previously written about how much I like it.

Using the project

The primary reason for uploading the project is to make it possible to look at the code and perhaps copy parts of it. However, it may also be interesting to try it out with an unmodified version of the MVC Alloy templates.

While the download package for the MVC template doesn't contain the test project it actually contains all of its dependencies. Therefor all one has to do to use it with the Alloy project is to extract it to the solution root folder and include the project in the solution.

In order to run the specifications a MSpec compatible test runner is needed. There's a console runner available but I'd recommend either TestDriven.NET or the runner that's included with ReSharper.

Interfaces implemented by shared blocks

$
0
0

From BlockData and ContentData, implemented by blocks when used as properties as well:

  • IContentData
  • IInitializableContent
  • IModifiedTrackable
  • IReadOnly
  • IReadOnly<BlockData>

From ContentMixin, shared blocks only:

  • IContent
  • IChangeTrackable
  • IExportable
  • IVersionable
  • ILocalizable
  • IContentMixin
  • IContentSecurable
  • ISecurable
  • IResourceable
  • ICategorizable

From the proxy class generated by Castle DynamicProxy:

  • IProxyTargetAccessor

Retrieve full result set with EPiServer Find

$
0
0
By default only the first ten hits are returned when using EPiServer Find. That number can be increased to a thousand, but not more. Here's an example of how to get *all* hits. Use with caution :)
public IEnumerable<PageData> GetPages()
{
    int totalNumberOfPages;

    var query = SearchClient.Instance.Search<PageData>()
                                .Take(1000);

    var batch = query.GetContentResult();
    foreach (var page in batch)
    {
        yield return page;
    }

    totalNumberOfPages = batch.TotalMatching;

    var nextBatchFrom = 1000;
    while (nextBatchFrom < totalNumberOfPages)
    {
        batch = query.Skip(nextBatchFrom).GetContentResult();
        foreach (var page in batch)
        {
            yield return page;
        }
        nextBatchFrom += 1000;
    }
}

Custom rendering of content areas

$
0
0
How to render content areas without using the built in functionality in EPiServer. And how to maintain some sort of on-page-editing functionality.

Content areas is a great new feature in EPiServer 7. One reason for that is of course the obvious one – UI composition using blocks and partial views for pages.

From a developer’s perspective there’s also another reason to be happy about content areas – we finally have a property type that supports multiple page references. That is, beyond using content areas for UI composition we can also use them whenever we want to enable editors to add multiple references to content items.

A couple of examples where content areas can come in handy is lists of links in headers and footers and lists of related pages.

Another example, which I’ll use in this article, is if we have a list of tags where each tag is represented by a page.

Small list of links

What we need here is a property that contains references to those tag pages. As the value of a content area property is a list of references to content items they work perfectly for this.

All we need to do is add a content area property to the page type that should have a tags property and render it using the PropertyFor method, right? Not quite.

A There is no renderer message for each link in the list

We also need to create partial renderers for tag pages. If we’re dealing with pages of a type that may already have other partial renderers, such as “regular” pages that we want to add to a list of related pages or as links to the site’s header, we also need to use a rendering tag to match the content area with a specific renderer.

While that solution works it adds overhead both in terms of development time and (slightly) performance wise.

Rendering content areas ourselves

Luckily there’s a simpler solution. We can render the content are ourselves by iterating over the items in it.

<div class="tags">
@foreach (var tag in Model.CurrentPage.Tags.FilteredContents.OfType<PageData>())
{
   @Html.PageLink(tag)
}</div>

Simple and nice, right? There’s just one problem. The property isn’t editable in on page edit mode.

To fix that when dealing with properties of other types we can normally add edit attributes to the wrapping element.

<div class="tags" @Html.EditAttributes(x => x.CurrentPage.Tags)>
@foreach (var tag in Model.CurrentPage.Tags.FilteredContents.OfType<PageData>())
{
   @Html.PageLink(tag)
}</div>

When rendering content areas ourselves applying edit attributes to the wrapping element does make the content area property editable, but EPiServer’s client side editing functionality can’t recognize that there’s anything in the content area.

Content area with overlay as if it was empty

Replicating native content area editing

In order to fix that we need to tell EPiServer about what content item each link, or whatever element we’re rendering in our loop, represents.

This is done by adding an attribute named data-epi-property-name to such elements, or a wrapping element. It’s value should be the string representation of the content’s ContentLink property.

<div class="tags" @Html.EditAttributes(x => x.CurrentPage.Tags)>
@foreach (var tag in Model.CurrentPage.Tags.FilteredContents.OfType<PageData>())
{<span data-epi-block-id="@tag.ContentLink.ToString()">
      @Html.PageLink(tag)</span>
}</div>

With that in place EPiServer’s edit UI recognizes that there are contents in the content area and also what content each element represents.

List of links with standard content area editing overlays

Looks like it’s working. We’re not quite done yet though. When a property’s value is changed EPiServer updates it’s place in the DOM with what it believes should be there. In the case of a content area that’s the default content area rendering.

There is no renderer for 'page type name' messages

In order to make our own rendering be used instead we’ll have to force a full refresh of the page when the property is changed.

How to do that is a topics of its own considering that it can be done in multiple ways in both Web Forms and MVC. The simplest solution in an MVC view would be to add a line like this:

@Html.FullRefreshPropertiesMetaData(new [] { "Tags"})

Dialog only editing

One issue with the solution described above is that the rendering of the content area in edit mode will differ from how it will actually look, at least when dealing with small elements such as links. EPiServer adds a minimum width of a hundred pixels to each element that represents an item in a content area.

While we could possibly fix that, editing a list of links or other small elements the same way as we do with a “real” content area used with blocks and stuff may not be the best of ideas. It looks a bit cluttered.

Also, if we order the items in the area in some other way than by the order in the area editors will perceive that they can change the order while they can't.

One option to handle this is to disable the on-page drag-n-drop editing of the property while keeping the dialog for editing that normally shows in a right hand panel when clicking on a content area.

To do that we can first disable the standard editor for content areas for the property by adding a UI hint to it.

[UIHint("DialogOnly")]
public virtual ContentArea Tags { get; set; }

We then  create an editor descriptor that half-way re-enables the standard content area editing for properties with that UI hint. Optionally, we can also position the dialog close to the property by specifying “uiWrapperType”.

[EditorDescriptorRegistration(
   TargetType = typeof(ContentArea), UIHint = "DialogOnly")]
public class FormsOnlyContentAreaEditing : EditorDescriptor
{
   public override void ModifyMetadata(
      ExtendedMetadata metadata, 
      IEnumerable<Attribute> attributes)
   {
      base.ModifyMetadata(metadata, attributes);
      ClientEditingClass = "epi.cms.contentediting.editors.ContentAreaEditor";
      metadata.CustomEditorSettings["uiWrapperType"] = "flyout";
   }
}

Now, when viewing the page in edit mode the property has the regular blue box around it. Editors can click on it an edit its contents in a dialog.

One nice aspect of this approach is that there’s no need to communicate things to EPiServer by using data attributes. That is, compared to trying to replicate the native content area editing we only need to add edit attributes and force a full page refresh when the property’s value changes.

A third option – faking it

Finally, we may want something in between the two solutions described above. I’m thinking about a solution in which the rendering isn’t affected and the display of the list isn’t cluttered by making the order of the items editable inline on the page but where it’s still possible to drag-n-drop new items into the list.

Adding item to content area using drag-n-drop

To accomplish this we don’t need any editor descriptor and we don’t need to add a UI hint to the property. We do need to clutter up the view’s code though.

<div @Html.EditAttributes(x => x.CurrentPage.Tags)>
   @if (PageEditing.PageIsInEditMode)
   {
      foreach (var tag in Model.CurrentPage.Tags.FilteredContents.OfType<PageData>())
      {<span style="display: none;" data-epi-block-id="@tag.ContentLink.ToString()"></span>
      }
   }
   @foreach (var tag in Model.CurrentPage.Tags.FilteredContents.OfType<PageData>())     {
      @Html.PageLink(tag)
   }</div>

As you can see, if we’re rendering the page in edit mode we’re iterating over the contents in the content area one more time. The first time we output hidden elements that EPiServer can use to determine what’s in the content area. The second time we render the contents “for real”.

This way we get the parts of the standard content area editing that we (may) like but hide the parts that would look cluttered in a small space and mess with the layout.

Heads up!

In the examples above we’ve rendered the contents of a content area property by iterating over its FilteredContents property. That’s a good thing as the FilteredContents exclude unpublished contents.

However, when displaying it to an editor we do want to include unpublished contents. Especially if we’re using one of the solutions where we allow some sort of inline editing of the area. That is, the two other solutions besides the dialog-only one.

If we don’t, any unpublished item in the area will be removed when editing the property as EPiServer replaces the existing items with whatever it finds in the DOM.

Therefor, we should have some logic that determines whether we should iterate over the content area’s FilteredContents or Contents property depending on what context we’re rendering it in. I’ve omitted that for the sake of brevity in the examples however.

Another thing I’ve done for the sake of brevity is hardcoding the data-epi-block-id attribute. To be on the safe side we should probably use the constant EPiServer.Editor.PageEditing.DataEPiBlockId instead.

Interfaces implemented by shared blocks

$
0
0

From BlockData and ContentData, implemented by blocks when used as properties as well:

  • IContentData
  • IInitializableContent
  • IModifiedTrackable
  • IReadOnly
  • IReadOnly<BlockData>

From ContentMixin, shared blocks only:

  • IContent
  • IChangeTrackable
  • IExportable
  • IVersionable
  • ILocalizable
  • IContentMixin
  • IContentSecurable
  • ISecurable
  • IResourceable
  • ICategorizable

From the proxy class generated by Castle DynamicProxy:

  • IProxyTargetAccessor

Retrieve full result set with EPiServer Find

$
0
0
By default only the first ten hits are returned when using EPiServer Find. That number can be increased to a thousand, but not more. Here's an example of how to get *all* hits. Use with caution :)
public IEnumerable<PageData> GetPages()
{
    int totalNumberOfPages;

    var query = SearchClient.Instance.Search<PageData>()
                                .Take(1000);

    var batch = query.GetContentResult();
    foreach (var page in batch)
    {
        yield return page;
    }

    totalNumberOfPages = batch.TotalMatching;

    var nextBatchFrom = 1000;
    while (nextBatchFrom < totalNumberOfPages)
    {
        batch = query.Skip(nextBatchFrom).GetContentResult();
        foreach (var page in batch)
        {
            yield return page;
        }
        nextBatchFrom += 1000;
    }
}

Hiding EPiServer's standard Category property

$
0
0
using System.Collections.Generic;
using EPiServer.Core;
using EPiServer.Shell.ObjectEditing;
using EPiServer.Shell.ObjectEditing.EditorDescriptors;

namespace MySite.EditorDescriptors
{
   [EditorDescriptorRegistration(TargetType = typeof(CategoryList))]
   public class HideCategoryEditorDescriptor : EditorDescriptor
   {
      public override void ModifyMetadata(
         ExtendedMetadata metadata,
         IEnumerable attributes)
      {
         base.ModifyMetadata(metadata, attributes);
         if (metadata.PropertyName == "icategorizable_category")
         {
            metadata.ShowForEdit = false;
         }
      }
   }
}

Moving it to a different tab, illustrated by the Settings tab in the example below:

using System.Collections.Generic;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.Shell.ObjectEditing;
using EPiServer.Shell.ObjectEditing.EditorDescriptors;

namespace MySite.EditorDescriptors
{
   [EditorDescriptorRegistration(TargetType = typeof(CategoryList))]
   public class HideCategoryEditorDescriptor : EditorDescriptor
   {
      public override void ModifyMetadata(
         ExtendedMetadata metadata,
         IEnumerable attributes)
      {
         base.ModifyMetadata(metadata, attributes);
         if (metadata.PropertyName == "icategorizable_category")
         {
            metadata.GroupName = SystemTabNames.Settings;
         }
      }
   }
}

For a specific page type only, illustrated as ArticlePage below:

using System.Collections.Generic;
using EPiServer.Core;
using EPiServer.Shell.ObjectEditing;
using EPiServer.Shell.ObjectEditing.EditorDescriptors;

namespace MySite.EditorDescriptors
{
   [EditorDescriptorRegistration(TargetType = typeof(CategoryList))]
   public class HideCategoryEditorDescriptor : EditorDescriptor
   {
      public override void ModifyMetadata(
         ExtendedMetadata metadata,
         IEnumerable attributes)
      {
         base.ModifyMetadata(metadata, attributes);

         dynamic mayQuack = metadata;
         var ownerContent = mayQuack.OwnerContent;
         if (ownerContent is ArticlePage
&& metadata.PropertyName == "icategorizable_category")
         {
            metadata.GroupName = SystemTabNames.Settings;
         }
      }
   }
}

For a specific page type only, without using dynamic but with reference to EPiServer.Cms.Shell.UI.dll (not recommended unless we're building an add-on):

using System.Collections.Generic;
using EPiServer.Cms.Shell.UI.ObjectEditing;
using EPiServer.Core;
using EPiServer.Shell.ObjectEditing;
using EPiServer.Shell.ObjectEditing.EditorDescriptors;

namespace MySite.EditorDescriptors
{
   [EditorDescriptorRegistration(TargetType = typeof(CategoryList))]
   public class HideCategoryEditorDescriptor : EditorDescriptor
   {
      public override void ModifyMetadata(
         ExtendedMetadata metadata,
         IEnumerable attributes)
      {
         base.ModifyMetadata(metadata, attributes);

         var contentMetadata = (ContentDataMetadata) metadata;
         var ownerContent = contentMetadata.OwnerContent;
         if (ownerContent is ArticlePage
&& metadata.PropertyName == "icategorizable_category")
         {
            metadata.GroupName = SystemTabNames.Settings;
         }
      }
   }
}

EPiServer and MVC – What is the view model?

$
0
0
Perhaps the most discussed and debated question of all when it comes to EPiServer and ASP.NET MVC development is “What is the view model?” Here's my thoughts on the subject and the reasoning behind the path chosen in the Alloy templates.

In my article about the MVC templates for EPiServer 7 I wrote

"Perhaps the most discussed question of all when it comes to EPiServer and MVC development is 'What is the model?' This is a multifaceted question, which could easily make this article very long. Hence, I will leave the background discussion for a later article or blog post"

In this article I'll follow up on that with both the background reasoning for what types of models are passed to views from controllers in the MVC templates, as well as general thoughs about (view) models when working with EPiServer CMS and ASP.NET MVC.

To use view models or not to use view models

Should we use a separate view model or simply pass PageData/BlockData to the view? In general ASP.NET MVC development it’s a well established best practice, at least in larger projects, to use separate view models. There are four commonly used arguments for this approach:

  1. By creating a model to the needs of the view we can keep the view simple and free from complex logic.
  2. By creating a view model we can create unit tests for the logic that takes domain models or other data as arguments and returns the data needed by the view.
  3. By using separate view models we can change the domain models without having to change the view models and vice versa.
  4. Model binding in ASP.NET MVC is vulnerable to mass assignment attacks and using a separate view model can prevent that.

All of the above arguments are of course valid but also context specific.

For instance, a page type is often already designed with the needs of the view in mind. We’re after all using a content management system in which the domain objects represent content intended to be displayed using templates.

That is, in many, but not all, scenarios a view, no matter if it’s MVC or Web Forms, simply renders properties from a PageData or BlockData object. In such cases there’s also no logic to test meaning that the second argument doesn’t make much sense in this context.

Using view models to be able to change domain models without having to update views, or vice versa, is an attractive idea from an architectural standpoint. It does come at a steep price however, as it requires a lot of code for mapping CMS content objects to view models.

While that can be handled by mapping frameworks such as AutoMapper we’d still be forced to write more code whenever we want to add a new property to a content type that should be displayed in the view, as we’d then have to update the page or block type as well as the view model.

Also, designing for future changes which may happen is a case of Big Design Up Front. Often the code that hasn’t been written yet is most easy to change.

Finally, while mass assignment is a very real threat when using ASP.NET MVC the model binding of CMS content is handled by EPiServer and EPiServer sites rarely have code in which non-trusted users are allowed to create or modify pages or blocks.

For these reasons the MVC template does allow them selves to use pages and blocks as view models when doing so offers the path of least resistance.

There is however another aspect of the underlying “What is the model” question – how to handle data needed by the overall layout rather than the specific view.

Framework components

Let’s review one of the pages from the Alloy site below.

It’s pretty obvious that the headline, text and form is unique for this page and therefor something that the rendering view should handle. But what about the header and footer?

There’s clearly quite a lot of data being displayed there but hardly any of it is related to the specific page. As for the breadcrumb and sub navigation those are both tied to the current pages location in the page tree but we can probably all agree that we wouldn’t like each page type specific controller or view to have to care about them.

While handling such “framework” components is straightforward using master pages and user controls in Web Forms there’s no clear-cut solution when using ASP.NET MVC. There are however a number of possible solutions which each can be grouped into one of two general patterns:

  1. Use a base class for view models with properties needed by the sites framework. Such properties can then be rendered in layout files, optionally using partial views.  
  2. Use child actions in layouts to render framework components such as navigations. Each child action, invoked by the Action or RenderAction HTML helpers, can then figure out the current page by looking at the request context.

Coming from Web Forms development where user controls is frequently used the second approach is attractive. By using child actions we can compose each page by using a number of stand alone components without any dependencies between them.

However, each child action will spawn a full MVC cycle, which is costly performance wise. Each such action would also have to figure out the current page context, which is rather tedious and possibly error prone. 

The first approach requires that all actions responsible for pages populate common properties of the view model. While that can be handled by using a base class for controllers with such actions we would end up with, possibly complex, inheritance hierarchies not only for view models but also for controllers.

View models in the MVC templates

Based on the reasoning about the necessity, or lack of such, for separate view models and the need for almost the exact same context in layouts and navigation components used in most views the solution in the MVC templates is to pass the current page object to the view along side data needed by framework components. This is done using a base class, or rather an interface to utilize generic variance.

IPageViewModel

All non-partial views and layouts in the MVC templates uses a model of type IPageViewModel<T>. The type parameter T has to be a descendent of PageData.

The interface defines a CurrentPage property of type T as well a couple of properties intended to provide context to layouts and framework components such as navigation elements.

The idea behind this solution is that all views, and especially their layout files, can rely on some common characteristics of the view model.

Populating context properties

To free controllers from having to populate these common properties of the view model an action filter named PageViewContextActionFilter is registered globally at start up. This filter inspects the view model which is about to be passed to the view and, given that it’s of type IPageViewModel, sets the common properties required by the sites framework.

By using this approach each action for a page can use it’s own specific view model as long as it implements IPageViewModel without having to care about populating common properties.

At the same time the logic for populating such common properties have been moved out from the sites controllers into a specific class responsible for framework components, which can be modified and tested without having to deal with controllers.

Should a specific controller or action want to influence the common properties of the view model it can do so by populating them on the view model as the filter then won’t modify them. Alternatively a controller can implement an interface named IModifyLayout that tells the filter to pass the Layout property of the view model to the controller after having populated it with default values.

Using an action filter instead of populating all properties of the view model in controllers makes the code less obvious and harder to follow. Clearly a bad thing. However, considering the alternative we'd either have to duplicate a lot of code or rely on inhertitance which would have almost the same negative effects while also tying controllers to a possibly complex inheritance hiearchy.

By taking the action filter route each controller can go on it's merry way focusing only on the specifics needed for the page type it handles.

Models for partial views

Partial views doesn't follow the pattern described above. Instead they either use a completely custom view model or the current content object is passed directly to the view.

The reason for not using a similar approach as for non-partial views is that the partial views don't have layouts and typically don't need to know about a broader context.

Conclusion

While the approach used in the MVC templates isn’t the most obvious and takes a while to wrap one’s head around it makes it easy to create new controller, models and views. As such I find it allows rapid development while maintaining decent separation of concerns.

When working with a view one has access to both view specific properties as well as a broader context. While this may seem like it's violating "best practices" I think it's good to keep in mind that best practices is dependent on context. And in the case of many EPiServer sites the context is that a page type very much is a view model in it self.

I've also used the same approach as in the MVC templates in a few other projects since and found it working well. Software design and development is always about context though. I can imagine sites where completely view-specific view models may makes sense. Similarly I can imagine sites where simply passing the current PageData object to the view would be an attractive solution.

Therefor I personally think that we can use the view model approach used in the MVC templates in most projects as a start. As with many other things we should however always look at the specific context of the project and think about how we can deliver most customer value and flexibility for evolving customer needs.

Viewing all 78 articles
Browse latest View live