A new type of PageList

by aanund 26. May 2011 01:36

A PageList where it is possible to define different Templates for different pagetypes. More of a proof-of-concept than anything else.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using EPiServer.Web.WebControls;
using System.Web.UI;
using EPiServer.Core;
using System.Collections.Specialized;
using System.Collections;
using System.ComponentModel.Design;
using System.ComponentModel;

namespace Custom.Web.Code
{
    [ParseChildren(true)]
    public class PageTypePageList : PageList
    {
        private PageTypeTemplateCollection _pageTypeTemplates;

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public List<PageTypeTemplateContainer> PageTemplates
        {
            get
            {
                if (_pageTypeTemplates == null)
                {
                    _pageTypeTemplates = new PageTypeTemplateCollection();
                }

                return _pageTypeTemplates;
            }
        }

        protected override void CreateChildControls()
        {
            if (ItemTemplate != null)
            {
                PageDataCollection pages = base.GetPages();
                if (pages.Count != 0)
                {
                    PageData page = null;
                    if (!PageReference.IsNullOrEmpty(this.PageLink))
                    {
                        page = this.GetPage(this.PageLink);
                    }
                    
                    if (HeaderTemplate != null)
                    {
                        Control container = new PageTemplateContainer(page);
                        HeaderTemplate.InstantiateIn(container);
                        this.Controls.Add(container);
                    }
                    
                    this.PreparePagingControls(pages);

                    for (int i = 0; i < pages.Count; i++)
                    {
                        Control control2 = new PageTemplateContainer(pages[i]);

                        ITemplate template = GetTemplateForPageData(pages[i]);
                        template.InstantiateIn(control2);

                        this.Controls.Add(control2);
                    }
                    
                    if (FooterTemplate != null)
                    {
                        Control control3 = new PageTemplateContainer(page);
                        FooterTemplate.InstantiateIn(control3);
                        this.Controls.Add(control3);
                    }

                    this.CreatePagingControls(pages);
                }
            }
        }

        protected ITemplate GetTemplateForPageData(PageData pd)
        {
            var template = PageTemplates.SingleOrDefault(t => t.PageTypeId == pd.PageTypeID);

            if (template == null)
            {
                template = PageTemplates.SingleOrDefault(t => StringComparer.OrdinalIgnoreCase.Compare(t.PageTypeName, pd.PageTypeName) == 0);

                if (template == null)
                {
                    return ItemTemplate;
                }
            }

            return template.ItemTemplate;
        }
    }

    [DefaultProperty("ItemTemplate"), ParseChildren(true)]
    public class PageTypeTemplateContainer
    {
        public int PageTypeId { get; set; }

        public string PageTypeName { get; set; }

        [TemplateContainer(typeof(PageTemplateContainer)), PersistenceMode(PersistenceMode.InnerProperty)]
        public ITemplate ItemTemplate { get; set; }
    }

    public class PageTypeTemplateCollection : List<PageTypeTemplateContainer>
    {
    }
}

With will be used as follows in the aspx:

<Custom:PageTypePageList runat="server">
    <HeaderTemplate>
        <div class="searchResults"> 
    </HeaderTemplate>
    <ItemTemplate>
        <div class="item"> 
            <h2><EPiServer:Property runat="server" PropertyName="PageLink" /></h2> 
            <p><EPiServer:Property runat="server" PropertyName="MainIntro" /></p>
        </div> 
    </ItemTemplate>
    <PageTemplates>
        <Custom:PageTypeTemplateContainer PageTypeName="[Public]Article">
            <ItemTemplate>
                <div class="item" style="border: solid 1px red;"> 
                    <h2><EPiServer:Property runat="server" PropertyName="PageLink" /></h2> 
                    <p><EPiServer:Property runat="server" PropertyName="MainIntro" /></p>
                    <p>Publisert: <%# ((EPiServer.Core.PageData)Container.DataItem).Created.ToString("dd.MM.yyyy") %></p>
                </div> 
            </ItemTemplate>
        </Custom:PageTypeTemplateContainer>
        <Custom:PageTypeTemplateContainer PageTypeId="11">
            <ItemTemplate>
                <div class="item" style="border: solid 1px blue;"> 
                    <h2><EPiServer:Property runat="server" PropertyName="PageLink" /></h2> 
                    <p><EPiServer:Property runat="server" PropertyName="MainIntro" /></p>
                    <p>Publisert: <%# ((EPiServer.Core.PageData)Container.DataItem).Created.ToString("dd.MM.yyyy") %></p>
                </div> 
            </ItemTemplate>
        </Custom:PageTypeTemplateContainer>
    </PageTemplates>
    <FooterTemplate>
        </div>
    </FooterTemplate>
</Custom:PageTypePageList>

It is admittedly a bit heavy on the control nesting.

Tags:

c# | episerver

RedirectRouteHandler

by aanund 12. February 2011 01:34

Continuing in the same topic as the languagehandler, this RouteHandler allows you to define ‘shortcuts’ outside of the Routing system (for instance in a database). To use it, you add a catch-all route with this RoutHandler (preferrably in conjunction with a special controller to handle 404’s).

IShortCutProvider shortCutProvider = ...;

Route possible404 = new Route(
    "*",
    new RouteValueDictionary(
        new { controller = "fault", action = "404" }
    ),
    new RedirectRouteHandler(shortCutProvider)
);

routes.Add(possible404);

Uses a simple interface to check for available shortcuts.

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace austrheim
{
    public class RedirectRouteHandler : MvcRouteHandler
    {
        private IShortCutProvider _shortCutProvider;

        public RedirectRouteHandler(IShortCutProvider shortCutProvider)
        {
            _shortCutProvider = shortCutProvider;
        }

        protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            string newUrl;

            if (_shortCutProvider.TryGetShortCut(requestContext.HttpContext.Request.Url, out newUrl))
            {
                return new RedirectHandler(newUrl);
            }

            return base.GetHttpHandler(requestContext);
        }
    }

    public interface IShortCutProvider
    {
        bool TryGetShortCut(Uri requestUri, out string newUrl);
    }

    public class RedirectHandler : IHttpHandler
    {
        private string newUrl;

        public RedirectHandler(string newUrl)
        {
            this.newUrl = newUrl;
        }

        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext httpContext)
        {
            httpContext.Response.StatusCode = 301;
            httpContext.Response.RedirectLocation = newUrl;
        }
    }
}

When a url is entered that does not match any routes, it will trickle down to the catch-all route.

The catch-all route then checks if a shortcut is found, and the viewer will be redirected to the shortcuts destination.

Tags:

mvc | c#

Forms login and friendly url

by aanund 30. January 2011 01:33

Sometimes (often) you need to switch the default EPiServer login page (~/util/login.aspx) with one of your own. And using EPiServer, sometimes you would like to use an EPiServer page (friendly url and all) as the login page. This can pose something of a problem.

This is caused by a combination of the Login control and the AuthenticationConfig (internal static class) which contains a method, that will do a string comparison on FormsAuthentication.LoginUrl  and Context.Request.Path (which will be the filename of the page).

To overcome this, you can for instance do…

<asp:Login ID="LoginControl" runat="server" OnLoggedIn="LoggedIn" />

And then in codebehind…

protected void LoggedIn(object sender, EventArgs e)
{
    Response.Redirect(GetRedirectUrl(), true);
}

private string GetRedirectUrl()
{
    string returnUrl = FormsAuthentication.GetRedirectUrl(string.Empty, false);

    if (!string.IsNullOrEmpty(returnUrl))
    {
        return returnUrl;
    }

    string str2 = LoginControl.DestinationPageUrl;

    if (!string.IsNullOrEmpty(str2))
    {
        return base.ResolveClientUrl(str2);
    }

    return FormsAuthentication.DefaultUrl;
}

Now your page will beautifylly redirect.

Tags:

c# | episerver

Localization and MVC

by aanund 20. July 2010 08:16

An oft-written about subject, with several solutions. In our specific case we wanted a solution where the language part of the localization was gathered from the url, like ~/no/.

To do this we subclassed MvcRouteHandler like this:

public class LanguageRouteHandler : MvcRouteHandler
{
    protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        string language = requestContext.RouteData.Values["language"] as string;

        if (!string.IsNullOrEmpty(language))
        {
            CultureInfo info = new CultureInfo(language);

            Thread.CurrentThread.CurrentUICulture = info;
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(info.Name);
        }

        return base.GetHttpHandler(requestContext);
    }
}

When a route using this handler is triggered, it sets the CurrentCulture and CurrentUICulture for us, so that all calls to ResourceProviders will get correctly localized values.

To use this you must also modify how your routes are mapped.

Route defaultRoute = new Route(
     "{language}/{controller}/{action}/{id}",
     new RouteValueDictionary(
         new
         {
             language = "no",
             controller = "Home",
             action = "Index"
         }),
     new LanguageRouteHandler());
routes.Add("defaultRoute", defaultRoute);

You might also notice that we use ‘named’ routes as suggested by Chad Moran in http://www.chadmoran.com/blog/2009/4/21/optimizing-url-generation-in-aspnet-mvc-part-1.html.

Predicate based filter for EPiServer

by aanund 17. July 2010 01:06

Today I needed to do some basic filtering of a PageDataCollection in a new EPiServer site. And I thought I would hack together a simple IPageFilter to do the job.

Then it occurred to me that this would be easy to handle if using LINQ. So I took a look a what I had just created and decided to rewrite it instead.

public class PredicateFilter : IPageFilter
{
    private readonly Predicate<PageData> _predicate;

    public PredicateFilter(Predicate<PageData> predicate)
    {
        _predicate = predicate;
    }

    public void Filter(object sender, FilterEventArgs e)
    {
        Filter(e.Pages);
    }

    public void Filter(PageDataCollection pages)
    {
        for (int i = pages.Count - 1; i >= 0; i--)
        {
            if (ShouldFilter(pages[i]))
            {
                pages.RemoveAt(i);
            }
        }
    }

    public bool ShouldFilter(PageData page)
    {
        return _predicate(page);
    }
}

Usage is something like this.

new PredicateFilter(p => p["PageShortcutLink"] != null).Filter(pages);

About me

I work for EDB Consulting Group in Oslo, Norway.

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010