Posted in: Comments

Every website should have a friendly 404 page. It’s also nice to give your clients the possibility to edit the content of the 404 page and maybe add a contact form as well. But how do we do it?

First we need a model for the 404 page:

namespace DV.Models
{
    using EPiServer.Core;
    using EPiServer.DataAbstraction;
    using EPiServer.DataAnnotations;

    [ContentType(
        GUID = "7ae4e3c3-d873-4e0b-a4ca-be7435bfd124")]
    [AvailableContentTypes(
       Availability = Availability.None)]
    public class NotFoundPage : PageData
    {
        public override void SetDefaultValues(ContentType contentType)
        {
            base.SetDefaultValues(contentType);

            this.VisibleInMenu = false; 
        }
    }
}

Then we need a template for the page and more importantly, we need to set the status code to 404:

namespace DV.Views
{
    using EPiServer;
    using EPiServer.Editor;
    using log4net;
    using System;
    using DV.Models;

    public partial class NotFound : TemplatePage<NotFoundPage>
    {
        private static readonly ILog Log = LogManager.GetLogger(typeof(NotFound));

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            // Only set status code on GET and in view mode
            if (!PageEditing.PageIsInEditMode && Request.HttpMethod == "GET")
            {
                Log.InfoFormat("404 {0}", Request.QueryString["aspxerrorpath"] ?? Request.RawUrl);

                this.Response.Status = "404 not found";
                this.Response.StatusCode = 404;
            }
        }
    }
}

It’s also good practice to log all the requests that are not found, this way we can determine if we need to set up rewrite rules for some of the missing resources. You would probably do this with Google Analytics as well. Notice that Request.RawUrl will contain the actual requested url!

Now we need to tell the application to route all 404’s to this page. This can be done by redirecting to a hard-coded url, e.g. /page-not-found/ or /views/notfound.aspx. But that’s not that nice. A better way is to just execute the url to the 404 page and keep the requested url in the address bar. This is easily done with some configuration inside the system.webServer section of web.config:

<system.webServer>
	<httpErrors errorMode="Custom">
		<remove statusCode="404" />
		<error statusCode="404" path="/Views/NotFound.aspx" responseMode="ExecuteURL" />
	</httpErrors>
</system.webServer>

The important part here is the responseMode. You could set it to just Redirect, but it’s much more clean to just execute the 404 page and keep the requested url. Then we need to turn off EPiServer’s error handling:

<episerver>
	<applicationSettings globalErrorHandling="Off" />
</episerver>

Best practice is to also to configure customErrors inside system.Web:

<system.web>
	<customErrors defaultRedirect="/Views/NotFound.aspx" mode="Off" redirectMode="ResponseRewrite">
		<error statusCode="404" redirect="/Views/NotFound.aspx" />
	</customErrors>
</system.web>

We do not have ExecuteURL here, but ResponseRewrite is the equivalent. Now all the 404’s will execute our template instead. But wait a minute, the content is not loading! That’s because the url cannot be resolved to any content, hence the 404. So we need to tell which page to actually load. In this case we will add a property to the start page, so an editor can select the 404 page:

[AllowedTypes(new[] { typeof(NotFoundPage) })]
public virtual PageReference NotFoundPage { get; set; }

And in the code-behind of our 404 template we just load that page in the OnPreInit event:

protected override void OnPreInit(EventArgs e)
{
    base.OnPreInit(e);

    var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
    var startPage = contentLoader.Get<StartPage>(ContentReference.StartPage);

    if (!PageEditing.PageIsInEditMode && !ContentReference.IsNullOrEmpty(startPage.NotFoundPage))
    {
        this.CurrentPageLink = startPage.NotFoundPage;
    }
}

Voila! Now we have a working 404 page, where an editor can change the content of the page. There’s only one caveat, what happens if the website is globalized and we have more than one language? We need to load the 404 page in the correct language of course. This is a bit trickier and this is the best solution I came up with. Please let me know if there is a better way of doing this. In the code-behind of the template we just override the InitializeCulture event:

protected override void InitializeCulture()
{
    if (!PageEditing.PageIsInEditMode)
    {
        // Try to find the language id from the actual requested url
        var segments = Request.RawUrl.Split('/');
        var languageSegment = segments.Length > 1 ? segments[1] : string.Empty;

        if (!string.IsNullOrWhiteSpace(languageSegment))
        {
            var languageMatcher = ServiceLocator.Current.GetInstance<ILanguageSegmentMatcher>();

            var languageId = string.Empty;

            if (languageMatcher.TryGetLanguageId(languageSegment, out languageId))
            {
                var currentLanguage = ServiceLocator.Current.GetInstance<IUpdateCurrentLanguage>();

                // Update the language, if language id was found
                currentLanguage.UpdateLanguage(languageId);
            }
        }
    }

    base.InitializeCulture();
}

We’re parsing out the first segment of the requested url, then we’re trying to match that segment with all available language versions of the page, including fallback settings. The correct language will be loaded automatically, if we have a domain for the language and not a segment in the url, but that’s not always the case.

Posted in: Comments

I’m still on my quest for clean markup and a nice editor experience. Please see my previous post how about to handle wrapping links. In this post I will cover a way to add wrapper markup to properties, that is only rendered in edit mode or when the properties have values. Notice that this will work for multiple properties, since quite often we have markup that wraps more than one property. So why would we even bother about this, you might ask? Well, sometimes we have styling that will apply to the wrapping markup, that we don’t want to show if the properties doesn’t have a value.

I don’t think there is much to say about the code, it’s very self-explanatory, so here it comes:

namespace DV.WebControls
{
    using System;
    using System.ComponentModel;
    using System.Linq;
    using System.Web.UI;
    using System.Web.UI.WebControls;

    using EPiServer.Editor;
    using EPiServer.ServiceLocation;
    using EPiServer.Web;

    public enum EvaluationType
    {
        Any,
        All
    }

    /// <summary>
    ///     A web control that always renders all child controls in edit mode. In view mode all specified
    ///     properties' value is evaluated. If the evalutation is successful according to the specified EvaluationType,
    ///     all child controls are rendered. Otherwise no child controls are rendered.
    /// </summary>
    [ParseChildren(false)]
    [PersistChildren(true)]
    public class PropertyPlaceHolder : Control
    {
        /// <summary>
        ///     Initializes a new instance of the <see cref="PropertyPlaceHolder" /> class.
        /// </summary>
        public PropertyPlaceHolder()
        {
            this.EvaluationType = EvaluationType.Any;
        }

        /// <summary>
        ///     Gets or sets the type of the evaluation.
        /// </summary>
        public EvaluationType EvaluationType { get; set; }

        /// <summary>
        ///     Gets or sets the name of the property.
        /// </summary>
        [TypeConverter(typeof(StringArrayConverter))]
        public string[] PropertyName { get; set; }

        // ReSharper disable once UnusedAutoPropertyAccessor.Local
        private Injected<ControlRenderContextBuilder> ContextBuilder { get; set; }

        /// <summary>
        ///     Raises the <see cref="E:System.Web.UI.Control.Load" /> event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.EventArgs" /> object that contains the event data.</param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (this.Visible && this.PropertyName != null)
            {
                if (this.EvaluationType == EvaluationType.Any)
                {
                    this.Visible = this.PropertyName.Any(this.ShouldRenderControl);
                }

                if (this.EvaluationType == EvaluationType.All)
                {
                    this.Visible = this.PropertyName.All(this.ShouldRenderControl);
                }
            }
        }

        private bool ShouldRenderControl(string propertyName)
        {
            var propertyContext = this.ContextBuilder.Service.BuildContext(this, propertyName);

            if (PageEditing.PageIsInEditMode)
            {
                return propertyContext.IsEditable();
            }

            var property = propertyContext.PropertyContainer.Property[propertyName];

            // In view mode, we only need to check if the property
            // has a value or not.
            return property != null && property.IsNull == false;
        }
    }
}

Here are some examples on how we can use the control

In this first example we want to wrap a content area with teaser blocks:

<DV:PropertyPlaceHolder PropertyName="Teasers" runat="server">
	<div class="teaser-listing">
		<div class="wrapper">
			<EPiServer:Property PropertyName="Teasers" CssClass="m-cols-2" runat="server">
				<RenderSettings ChildrenCssClass="m-col" />
			</EPiServer:Property>
		</div>
	</div>
</DV:PropertyPlaceHolder>

In this second example we want to create a definition list of a bunch of contact information properties, notice that the dl element will get rendered if any of the specified properties have a value:

<DV:PropertyPlaceHolder PropertyName="Department,Office,Email,Telephone" EvaluationType="Any" runat="server">
	<dl>
		<DV:PropertyPlaceHolder PropertyName="Department" runat="server">
			<dt>Department</dt>
			<EPiServer:Property PropertyName="Department" CustomTagName="dd" runat="server" />
		</DV:PropertyPlaceHolder>

		<DV:PropertyPlaceHolder PropertyName="Office" runat="server">
			<dt>Office</dt>
			<EPiServer:Property PropertyName="Office" CustomTagName="dd" runat="server" />
		</DV:PropertyPlaceHolder>

		<DV:PropertyPlaceHolder PropertyName="Email" runat="server">
			<dt>Email</dt>
			<EPiServer:Property PropertyName="Email" CustomTagName="dd" runat="server" />
		</DV:PropertyPlaceHolder>

		<DV:PropertyPlaceHolder PropertyName="Telephone" runat="server">
			<dt>Telephone</dt>
			<EPiServer:Property PropertyName="Telephone" CustomTagName="dd" runat="server" />
		</DV:PropertyPlaceHolder>
	</dl>
</DV:PropertyPlaceHolder>

Posted in: Comments

First off, there is a similar solution already in the Alloy templates. But this one is a bit more refined.

So the problem. Lets say you have a block that represents a teaser, the teaser has a tagline, heading, image and link. When the editor has specified a link, the tagline and heading should be wrapped with the link, otherwise they should be wrapped with a div. Something like this:

<div class="teaser">
	<div class="image-wrapper">
		<img src="/image.jpg" alt="" />
	</div>
	<a class="content-wrapper" href="/about-us/">
		<p>Tagline</p>
		<h2>Heading</h2>
	</a>
</div>

And without a link:

<div class="teaser">
	<div class="image-wrapper">
		<img src="/image.jpg" alt="" />
	</div>
	<div class="content-wrapper">
		<p>Tagline</p>
		<h2>Heading</h2>
	</div>
</div>

There is also the case when the teaser should be edited, the link can cause problems for the editors and it’s best to replace it then as well.

This can be solved with a lot of if-statements in the markup and check if the link is null and/or you’re in edit mode, maybe in a combination with placeholder controls with the markup that should be toggled. It will get messy though. Something like this:

<div class="teaser">
	<div class="image-wrapper">
		<EPiServer:Property runat="server" PropertyName="Image" />
	</div>
	<asp:PlaceHolder runat="server" Visible="<%# CurrentBlock.Link != null && PageEditing.PageIsInEditMode == false %>">
		<a class="content-wrapper" href="<%= CurrentBlock.Link  %>">
	</asp:PlaceHolder>

	<asp:PlaceHolder runat="server" Visible="<%# CurrentBlock.Link == null || PageEditing.PageIsInEditMode %>">
		<div class="content-wrapper">
	</asp:PlaceHolder>

		<EPiServer:Property runat="server" PropertyName="Tagline" CustomTagName="p" />
		<EPiServer:Property runat="server" PropertyName="Heading" CustomTagName="h2" />

	<asp:PlaceHolder runat="server" Visible="<%# CurrentBlock.Link == null || PageEditing.PageIsInEditMode %>">
		</div>
	</asp:PlaceHolder>

	<asp:PlaceHolder runat="server" Visible="<%# CurrentBlock.Link != null && PageEditing.PageIsInEditMode == false %>">
		</a>
	</asp:PlaceHolder>
</div>

Instead we can create a web control that handles this. We can then use this web control to wrap the content. If we have specified a tag name in CustomTagName, this tag will be rendered instead of the link if the link is null or we’re in edit mode, otherwise no tag will be rendered.

namespace DV.Controls
{
    using EPiServer;
    using EPiServer.Editor;
    using System;
    using System.Web.UI;
    using System.Web.UI.WebControls;

    /// <summary>
    /// A web control that renders a hyperlink when not in edit mode and the specified url is not null.
    /// If in edit mode or the url is null, a tag of the type specified in CustomTageName is rendered instead.
    /// If no tag is specified in CustomTageName, no tag is rendered at all.
    /// </summary>
    [ParseChildren(false)]
    [PersistChildren(true)]
    public class ToggleLink : WebControl
    {
        public Url Url { get; set; }

        public string CustomTagName { get; set; }

        private bool RenderCustomTag
        {
            get
            {
                return PageEditing.PageIsInEditMode || this.Url == null;
            }
        }

        protected override HtmlTextWriterTag TagKey
        {
            get
            {
                if (this.RenderCustomTag)
                {
                    if (string.IsNullOrEmpty(this.CustomTagName))
                    {
                        return HtmlTextWriterTag.Unknown;
                    }
                    else
                    {
                        return (HtmlTextWriterTag)Enum.Parse(
                            typeof(HtmlTextWriterTag),
                            this.CustomTagName,
                            true);
                    }
                }

                return HtmlTextWriterTag.A;
            }
        }

        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            base.AddAttributesToRender(writer);

            if (this.TagKey == HtmlTextWriterTag.A)
            {
                writer.AddAttribute(HtmlTextWriterAttribute.Href, this.Url.ToString());
            }
        }

        public override void RenderBeginTag(HtmlTextWriter writer)
        {
            if (this.RenderCustomTag &&
                this.TagKey == HtmlTextWriterTag.Unknown)
            {
                return;
            }

            base.RenderBeginTag(writer);
        }

        public override void RenderEndTag(HtmlTextWriter writer)
        {
            if (this.RenderCustomTag &&
                this.TagKey == HtmlTextWriterTag.Unknown)
            {
                return;
            }

            base.RenderEndTag(writer);
        }
    }
}

Now we can write like this instead:

<div class="teaser">
	<div class="image-wrapper">
		<EPiServer:Property runat="server" PropertyName="Image" />
	</div>
	<DV:ToggleLink Url="<%# CurrentBlock.Link %>" CustomTagName="div" CssClass="content-wrapper" runat="server">
		<EPiServer:Property runat="server" PropertyName="Tagline" CustomTagName="p" />
		<EPiServer:Property runat="server" PropertyName="Heading" CustomTagName="h2" />
	</DV:ToggleLink>
</div>

Much nicer code, if you ask me.

Posted in: Comments

I was using a regular EPiServer template to generate an email template, which an email service could download and send to subscribers. The template had some dynamic links, e.g. unsubscribe and preferences links. These are defined with tokens in the markup and are replaced when the email is sent by the service. The unsubscribe link looks like:

<a href="%UNSUBSCRIBELINK%">Unsubscribe</a>

Unfortunately this links gets rewritten by the URL rewriter and/or html parser that the rewriter is using.

I was trying to exclude the links and the template in Global by adding them in:

UrlRewriteProvider.AddExcludedPath() 

That did however not work. The links were always rendered like:

<a href="/path/to/template/%UNSUBSCRIBELINK%">Unsubscribe</a>

Thanks to Tobias and an old blog post, we found a workaround. You can add an attribute to the link which stops the rewriter from touching the link. The important part here, besides the attribute, is to add an id to the element as well(!).

<a href="%UNSUBSCRIBELINK%" <%= EPiServer.Web.UrlRewriteProvider.UrlPreventRewriteAttribute %>="true" id="required-id-but-not-used">Unsubscribe</a>

Posted in: Comments

Alright, so it turns out EPiServer 7 (and later versions) already has a route for permanent links. It was slightly different from the one I created, so here’s an updated version of the extension method:

public static string PermanentLink(this PageData page)
{
    var url = new UriBuilder(Settings.Instance.SiteUrl);

    var languageBranch = ServiceLocator.Current.GetInstance<ILanguageBranchRepository>()
        .Load(page.Language);

    url.Path = string.Format("/{0}/link/{1}",
        languageBranch.CurrentUrlSegment,
        page.ContentGuid.ToString("N"));

    return url.Uri.AbsoluteUri;
}

So no need to register your own route.