<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by Johan Pettersson</title><link href="http://world.optimizely.com" /><updated>2022-05-17T11:49:12.0000000Z</updated><id>https://world.optimizely.com/blogs/Johan-Pettersson/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>Content development improvements May &#39;22</title><link href="https://world.optimizely.com/blogs/Johan-Pettersson/Dates/2022/5/content-development-improvements-may-21/" /><id>&lt;p&gt;Lately we have had a slew of notable releases that we would like to highlight. The work of migrating packages to .NET Core continues and more packages are now cross-compiled to .NET5 and .NET6. Note that you can target .NET6 even though not all packages are cross-compiled yet. You can follow our work here &lt;a href=&quot;/link/42b7913c321d4886b468000231d9baa4.aspx&quot;&gt;https://world.optimizely.com/resources/net5/add-ons/&lt;/a&gt;. Clicking on an add-on will take you to the NuGet package details page where you can subscribe to and get notified when it&amp;rsquo;s updated.&lt;/p&gt;
&lt;h2&gt;Updated Templates&lt;/h2&gt;
&lt;p&gt;This work is now reflected in &lt;a href=&quot;https://www.nuget.org/packages/EPiServer.Templates/&quot;&gt;our templates&lt;/a&gt;. Latest version (1.2) is now targeting .NET6 and uses the latest dependencies. You should be presented with the following message if you have an outdated version installed when trying to apply a template:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;An update for template package &#39;EPiServer.Templates::1.1.0&#39; is available.
To update the package use:
   dotnet new --install EPiServer.Templates::1.2.0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also do a manual check for updated versions:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;dotnet new --update-check&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to stay on top of what happens in our templates, we recommend staring the GitHub repository at &lt;a href=&quot;https://github.com/episerver/content-templates&quot;&gt;https://github.com/episerver/content-templates&lt;/a&gt;. We recommend doing this even though you do not use the templates, since we will continue to update them with the latest syntax and configuration that you also might want to use in your own templates or projects.&lt;/p&gt;
&lt;p&gt;Note that we do not support the new host configuration model yet. This means you still need to have a separate startup class and cannot use &lt;code&gt;WebApplication&lt;/code&gt; and &lt;code&gt;WebApplicationBuilder&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Content Delivery Search&lt;/h2&gt;
&lt;p&gt;Search and Navigation (formerly known as Find) is now supported in Content Delivery again. See &lt;a href=&quot;https://docs.developers.optimizely.com/content-cloud/v1.5.0-content-delivery-api/docs/content-search-api&quot;&gt;https://docs.developers.optimizely.com/content-cloud/v1.5.0-content-delivery-api/docs/content-search-api&lt;/a&gt; for documentation.&lt;/p&gt;
&lt;h2&gt;OpenID Connect UI&lt;/h2&gt;
&lt;p&gt;Last year we released support for OpenID Connect, see &lt;a href=&quot;https://docs.developers.optimizely.com/content-cloud/v1.5.0-content-delivery-api/docs/api-authentication&quot;&gt;https://docs.developers.optimizely.com/content-cloud/v1.5.0-content-delivery-api/docs/api-authentication&lt;/a&gt; for the documentation. The initial release also included a small UI for managing refresh tokens. This UI is now updated to include the possibility to manage applications without the need to deploy code. Applications that are defined in code, or via configuration, are also displayed but only as read-only.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/1ec8f95663554d0fafbf6a27334d7832.aspx&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;New sample site in JS SDK&lt;/h2&gt;
&lt;p&gt;The old sample site has been updated to .NET6 and we have also made a new sample available, see &lt;a href=&quot;https://github.com/episerver/content-delivery-js-sdk/tree/master/samples/music-festival-vue-coupled&quot;&gt;https://github.com/episerver/content-delivery-js-sdk/tree/master/samples/music-festival-vue-coupled&lt;/a&gt;. This sample is hosting the client app, built with Vue CLI, in the same dotnet process. But during development the front-end app is hosted in a separate Node.js process and the backend is proxied to the dotnet process using the new ASP.NET SPA Proxy. This means you get all the nice Node.js tooling, such as webpack&amp;rsquo;s hot reload, during development.&lt;/p&gt;</id><updated>2022-05-17T11:49:12.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>New templates and “getting started” experience</title><link href="https://world.optimizely.com/blogs/Johan-Pettersson/Dates/2022/3/new-templates-and-getting-started-experience/" /><id>&lt;p&gt;Since we released CMS 12 and Commerce 14 our &amp;ldquo;getting started&amp;rdquo; experience has been lacking a bit. We decided to not upgrade our Visual Studio extension and instead rely on the new &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-new&quot;&gt;dotnet new experience&lt;/a&gt;. This meant that we had no item templates or the old trusty Alloy sample site available for CMS 12. We initially released two templates to create empty Content Cloud or Commerce Cloud projects and a CLI tool to create and manage databases. But we felt we could improve this experience further.&lt;/p&gt;
&lt;p&gt;First, we decided to open source our templates so the community &amp;ndash; you &amp;ndash; can contribute and report issues. The new templates are now located on GitHub at &lt;a href=&quot;https://github.com/episerver/content-templates&quot;&gt;https://github.com/episerver/content-templates&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When you &lt;a href=&quot;/link/d3c54953c2514845b0260072d15b5ddc.aspx&quot;&gt;install the templates&lt;/a&gt; they will be available from the command line as well as show up in the &amp;ldquo;New project dialog&amp;rdquo; in Visual Studio 2022 and later. In Visual Studio 2019 this can be enabled by toggling a preview flag, see &lt;a href=&quot;https://devblogs.microsoft.com/dotnet/net-cli-templates-in-visual-studio/&quot;&gt;https://devblogs.microsoft.com/dotnet/net-cli-templates-in-visual-studio/&lt;/a&gt;. Unfortunately, the item templates are not available in Visual Studio yet, but there are plans from Microsoft to enable this in the .NET 7 timeframe.&lt;/p&gt;
&lt;p&gt;Install the templates by running following command:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;dotnet new --install EPiServer.Templates&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also set out to make the templates cross-platform by enabling basic Docker support. The Docker support is not meant to be used when deploying the application, but rather as an effortless way to get started without the need to install Microsoft SQL Server.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;/link/a05229aa15ed4dbf8fb34edd6d39a87d.aspx&quot;&gt;Alloy templates&lt;/a&gt; also got a bit of a refresh. We updated Bootstrap to the latest version and updated the look and feel to be a bit more modern.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/f5545d9c2c544725a63dae8033469f05.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This template previously contained code for creating an administrator user on startup, we have now added this capability in the product instead. This is enabled in all new templates. On first request you are redirected to a view where you can create the first administrator user. This is only enabled on loopback addresses (localhost) and when no user exists.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/dd5249213459461495bd69ea66ebe985.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The following project templates are available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Alloy (epi-alloy-mvc)&lt;br /&gt;Demonstration template. The Alloy template contains a site for a fictional company, Alloy Technologies, to demonstrate the most notable features of Optimizely Content cloud and gives you a site that is ready to explore and to learn from.&lt;/li&gt;
&lt;li&gt;Empty Content Cloud (epi-cms-empty)&lt;/li&gt;
&lt;li&gt;Empty Commerce Cloud (epi-commerce-empty)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And the following item templates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Content component (epi-cms-contentcomponent)&lt;br /&gt;View component for blocks and partial content.&lt;/li&gt;
&lt;li&gt;Content Type (epi-cms-contenttype)&lt;/li&gt;
&lt;li&gt;Initialization Module (epi-cms-initializationmodule)&lt;/li&gt;
&lt;li&gt;Scheduled Job (epi-cms-job)&lt;/li&gt;
&lt;li&gt;Page Controller (epi-cms-pagecontroller)&lt;/li&gt;
&lt;li&gt;Razor Page (epi-cms-razorpage)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To use a template, run following command:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;dotnet new epi-alloy-mvc --name My.Web&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The name option is optional. If you don&#39;t provide one, the directory name will be used instead.&lt;/p&gt;
&lt;p&gt;To see a list of all available options that are template-specific, run following command:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;dotnet new epi-alloy-mvc --help&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;Optimizely Alloy MVC (C#)
Author: Episerver AB
Description: Example MVC application for testing and learning Optimizely CMS.
Options:
  --enable-docker  Enable Docker support
                   bool - Optional
                   Default: false

  --sa-password    The password the SA database account should have
                   string - Optional
                   Default: Qwerty12345!&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hope this helps and improves your experience working with our products. Don&amp;rsquo;t hesitate to report any issues or suggestions on GitHub at &lt;a href=&quot;https://github.com/episerver/content-templates&quot;&gt;https://github.com/episerver/content-templates&lt;/a&gt;. Also, look out for an update of the templates soon with full .NET6 support.&lt;/p&gt;</id><updated>2022-03-24T17:01:17.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Prevent editors from deleting &quot;system&quot; content</title><link href="http://www.dodavinkeln.se/post/prevent-editors-from-deleting-system-content" /><id>
	&lt;p&gt;Sometimes you have content, e.g. pages, that&#39;s necessary for the website to run. This could be the startpage, news archive page, search page and so on. Let&#39;s call them &#39;system content&#39;.&lt;/p&gt;

&lt;p&gt;Now, if you want to prevent your editors from accidentally deleting these pages you could change the permissions to these pages. The downside with using the regular content permissions is that you have to stop the inheritance of the permissions, and not just that, you also need to reset them again for every child page that you want the editors to able to delete.&lt;/p&gt;

&lt;p&gt;This gets unmanageable after a while.&lt;/p&gt;

&lt;p&gt;So what can we do instead? Well, we could hook in to the content events in the content repository and then stop them from being deleted.&lt;/p&gt;

&lt;p&gt;First we define an interface to mark all content types that should not be deleted.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;
namespace DV.ContentTypes.Interfaces
{
    using EPiServer.Core;

    /// &amp;lt;summary&amp;gt;
    ///     Decorate content types with this interface to classify them
    ///     as system content. System content should not be deleted by editors e.g..
    ///     We will stop editors from deleting these content items unless they have
    ///     access to that function. Please see &amp;lt;see cref=&quot;DV.DeleteSystemContentPermissions&quot;/&amp;gt;
    ///     and  &amp;lt;see cref=&quot;DV.SystemContentInitialization&quot;/&amp;gt;.
    /// &amp;lt;/summary&amp;gt;
    public interface ISystemContent : IContent
    {
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then we decorate our content types.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;
namespace DV.ContentTypes.Pages
{
    [ContentType]
    public class HomePage : PageData, ISystemContent
    {
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But hey, wouldn&#39;t it be nice if we could delete them anyway, in like special cases? Let&#39;s create special permissions, &lt;a href=&quot;/link/dc2b2e3bcefc4a7f94191f06f1cb069c.aspx&quot;&gt;Permission to Functions&lt;/a&gt; to the rescue.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;
namespace DV
{
    using EPiServer.DataAnnotations;
    using EPiServer.Security;

    [PermissionTypes]
    public static class DeleteSystemContentPermissions
    {
        static DeleteSystemContentPermissions()
        {
            DeleteContent = new PermissionType(nameof(DeleteSystemContentPermissions), nameof(DeleteContent));
            DeleteLanguageVersion = new PermissionType(nameof(DeleteSystemContentPermissions), nameof(DeleteLanguageVersion));
        }

        public static PermissionType DeleteContent { get; private set; }

        public static PermissionType DeleteLanguageVersion { get; private set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Please follow the guide in the link above to give the permissions more meaningfull descriptions by also creating translations.&lt;/p&gt;

&lt;p&gt;Now we can create an initializable module that will actully prevent editors from deleting &#39;system content&#39; or allow them if they have enough permissions.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;
namespace DV
{
    using EPiServer;
    using EPiServer.Core;
    using EPiServer.Framework;
    using EPiServer.Framework.Initialization;
    using EPiServer.Security;
    using EPiServer.ServiceLocation;

    [InitializableModule]
    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class SystemContentInitialization : IInitializableModule
    {
        public void Initialize(InitializationEngine context)
        {
            var contentEvents = ServiceLocator.Current.GetInstance&amp;lt;IContentEvents&amp;gt;();

            contentEvents.MovingContent += this.MovingContent;
            contentEvents.DeletingContentLanguage += this.DeletingContentLanguage;
        }

        public void Uninitialize(InitializationEngine context)
        {
            var contentEvents = ServiceLocator.Current.GetInstance&amp;lt;IContentEvents&amp;gt;();

            contentEvents.MovingContent -= this.MovingContent;
            contentEvents.DeletingContentLanguage -= this.DeletingContentLanguage;
        }

        private void MovingContent(object sender, ContentEventArgs e)
        {
            if (e.Content is ISystemContent == false)
            {
                return;
            }

            if (e.TargetLink.Equals(ContentReference.WasteBasket, true) == false)
            {
                return;
            }

            bool hasPermission = PrincipalInfo.Current.IsPermitted(DeleteSystemContentPermissions.DeleteContent);

            if (hasPermission == false)
            {
                e.CancelAction = true;
                e.CancelReason = &quot;You&#39;re not allowed to delete this content. You need permission to this function.&quot;;
            }
        }

        private void DeletingContentLanguage(object sender, ContentEventArgs e)
        {
            if (e.Content is ISystemContent == false)
            {
                return;
            }

            bool hasPermission = PrincipalInfo.Current.IsPermitted(DeleteSystemContentPermissions.DeleteLanguageVersion);

            if (hasPermission == false)
            {
                e.CancelAction = true;
                e.CancelReason = &quot;You&#39;re not allowed to delete this language. You need permission to this function.&quot;;
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The Version gadget will unfortunately not display our &lt;code&gt;CancelReason&lt;/code&gt; message when deleting a language version, but if you try to delete the content (master language), then our message will be displayed. I you&#39;re using the Language Add-on, then the add-on will just throw an error when you delete a language. I wish that add-on would handle this better, but hey, it will at least stop the editors from deleting the language.&lt;/p&gt;
	</id><updated>2017-07-11T17:43:07.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Introducing Episerver Taxonomy</title><link href="http://www.dodavinkeln.se/post/introducing-episerver-taxonomy" /><id>
		&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/JohanPetersson/episerver-taxonomy/master/taxonomy.png&quot; width=&quot;300&quot; class=&quot;img-rounded pull-right&quot; alt=&quot;Screenshot from Episerver editing UI&quot; /&gt;Since I started working with Episerver back in 2008 I&#39;ve been missing better taxonomy support, that and a &quot;bucket&quot; feature where we can put content that doesn&#39;t fit into a content tree, but in this post I will just cover taxonomy (buckets might be my next challenge though).&lt;/p&gt;

		&lt;p&gt;If you&#39;re like me, you&#39;ve probably always wondered why you need admin access to just administer categories and why it&#39;s so cumbersome to translate them. Well, you don&#39;t have to anymore! Head over to GitHub and check my new project out at &lt;a href=&quot;https://github.com/JohanPetersson/episerver-taxonomy&quot;&gt;https://github.com/JohanPetersson/episerver-taxonomy&lt;/a&gt;.&lt;/p&gt;

		&lt;p&gt;You&#39;ll find more information at GitHub, but in short this Add-on gives editors a new UI to manage taxonomy and us, developers, a new way to define different types of taxonomy.&lt;p&gt;

		&lt;p&gt;We can define taxonomy by inheriting from &lt;code&gt;TaxonomyData&lt;/code&gt;:&lt;/p&gt;

		&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;
using Dodavinkeln.Taxonomy.Core;
using EPiServer.DataAnnotations;

[ContentType(GUID = &quot;974ebaf3-c6fc-4332-a809-344b7e372f21&quot;)]
public class CategoryData : TaxonomyData
{
}&lt;/code&gt;&lt;/pre&gt;

		&lt;p&gt;And then add taxonomy properties to content types:&lt;/p&gt;

		&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;
[Taxonomy]
[Display(Name = &quot;News category&quot;)]
public virtual ContentReference NewsCategory { get; set; }&lt;/code&gt;&lt;/pre&gt;

		&lt;p&gt;The Add-on is not release in a public feed yet, I would like to have some feedback and implement a few more features before releasing 1.0. So please check it out and leave comments here or on GitHub.&lt;/p&gt;
	&lt;/p&gt;&lt;/p&gt;</id><updated>2016-11-27T17:30:36.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>User picker property</title><link href="http://www.dodavinkeln.se/post/user-picker-property" /><id>&lt;p&gt;Lets say you want to give your editors the possibility to select a user (username) in a property so we can display e.g. a byline in an article. The simple solution is to just add a string property to your page type, but then the editor need to enter the username manually. Instead we can use the &lt;a href=&quot;/link/59557718dcdd44e7beabbb37e3a26572.aspx&quot;&gt;AutoSuggestSelection&lt;/a&gt; attribute, then we only need to implement an &lt;code&gt;ISelectionQuery&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Fortunately there is an API in Episerver we can use to query for users, &lt;code&gt;IQueryableNotificationUsers&lt;/code&gt;, but keep in mind that the API is internal and may change without notice.&lt;/p&gt;

&lt;p&gt;First we need to implement our query:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;
namespace DV
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using EPiServer.Notification;
    using EPiServer.ServiceLocation;
    using EPiServer.Shell.ObjectEditing;

    [ServiceConfiguration(typeof(ISelectionQuery))]
    public class UserSelectionQuery : ISelectionQuery
    {
        private readonly IQueryableNotificationUsers userRepository;

        public UserSelectionQuery(IQueryableNotificationUsers userRepository)
        {
            this.userRepository = userRepository;
        }

        public ISelectItem GetItemByValue(string value)
        {
            var task = Task.Run(async () =&amp;gt; await this.userRepository.FindAsync(value, 0, 1));

            task.Wait();

            if (task.Result.TotalCount == 0)
            {
                return null;
            }

            var user = task.Result.PagedResult.Single();

            return new SelectItem
            {
                Text = $&amp;quot;{user.DisplayName} ({user.UserName})&amp;quot;,
                Value = user.UserName
            };
        }

        public IEnumerable&amp;lt;ISelectItem&amp;gt; GetItems(string query)
        {
            var task = Task.Run(async () =&amp;gt; await this.userRepository.FindAsync(query, 0, 10));

            task.Wait();

            if (task.Result.TotalCount == 0)
            {
                return Enumerable.Empty&amp;lt;ISelectItem&amp;gt;();
            }

            return task.Result.PagedResult.Select(user =&amp;gt; new SelectItem
            {
                Text = $&amp;quot;{user.DisplayName} ({user.UserName})&amp;quot;,
                Value = user.UserName
            });
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then we can use it in the attribute:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;
[AutoSuggestSelection(typeof(UserSelectionQuery))]
[Display(Name = &quot;Published by&quot;)]
public virtual string PublishedBy { get; set; }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Hope this helps.&lt;/p&gt; 
  </id><updated>2016-10-13T18:29:47.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Constants for all Episerver icons</title><link href="http://www.dodavinkeln.se/post/constants-for-all-episerver-icons" /><id>&lt;p&gt;I couldn&#39;t find constants for all Episerver action icons located here &lt;a href=&quot;http://ux.episerver.com/&quot;&gt;http://ux.episerver.com/&lt;/a&gt; anywhere in Episerver or on the Internet. So I thought I would share those with you. These are really useful if you e.g. are using this &lt;a href=&quot;http://blog.nansen.com/2014/10/page-tree-icons-in-episerver-cms-75.html&quot;&gt;solution&lt;/a&gt;.&lt;/p&gt;
  &lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;
public static class ContentIcons
{
    public const string Clock = &quot;epi-iconClock&quot;;
    public const string Checkmark = &quot;epi-iconCheckmark&quot;;
    public const string Stop = &quot;epi-iconStop&quot;;
    public const string Versions = &quot;epi-iconVersions&quot;;
    public const string Revert = &quot;epi-iconRevert&quot;;
    public const string Undo = &quot;epi-iconUndo&quot;;
    public const string Redo = &quot;epi-iconRedo&quot;;
    public const string Pen = &quot;epi-iconPen&quot;;
    public const string PenDisabled = &quot;epi-iconPenDisabled&quot;;
    public const string DuplicatePage = &quot;epi-iconDuplicatePage&quot;;
    public const string Primary = &quot;epi-iconPrimary&quot;;
    public const string Trash = &quot;epi-iconTrash&quot;;
    public const string Users = &quot;epi-iconUsers&quot;;
    public const string Search = &quot;epi-iconSearch&quot;;
    public const string Plus = &quot;epi-iconPlus&quot;;
    public const string Minus = &quot;epi-iconMinus&quot;;
    public const string Star = &quot;epi-iconStar&quot;;
    public const string Settings = &quot;epi-iconSettings&quot;;
    public const string Lock = &quot;epi-iconLock&quot;;
    public const string Pin = &quot;epi-iconPin&quot;;
    public const string Page = &quot;epi-iconPage&quot;;
    public const string Folder = &quot;epi-iconFolder&quot;;
    public const string SharedBlock = &quot;epi-iconSharedBlock&quot;;
    public const string Up = &quot;epi-iconUp&quot;;
    public const string Down = &quot;epi-iconDown&quot;;
    public const string Reload = &quot;epi-iconReload&quot;;
    public const string Share = &quot;epi-iconShare&quot;;
    public const string Download = &quot;epi-iconDownload&quot;;
    public const string DnD = &quot;epi-iconDnD&quot;;
    public const string ContextMenu = &quot;epi-iconContextMenu&quot;;
    public const string Rename = &quot;epi-iconRename&quot;;
    public const string Left = &quot;epi-iconLeft&quot;;
    public const string Right = &quot;epi-iconRight&quot;;
    public const string User = &quot;epi-iconUser&quot;;
    public const string Hidden = &quot;epi-iconHidden&quot;;
    public const string NewWindow = &quot;epi-iconNewWindow&quot;;
    public const string Catalog = &quot;epi-iconCatalog&quot;;
    public const string Category = &quot;epi-iconCategory&quot;;
    public const string Product = &quot;epi-iconProduct&quot;;
    public const string SKU = &quot;epi-iconSKU&quot;;
    public const string Package = &quot;epi-iconPackage&quot;;
    public const string Bundle = &quot;epi-iconBundle&quot;;
    public const string Boxes = &quot;epi-iconBoxes&quot;;
    public const string References = &quot;epi-iconReferences&quot;;
    public const string Bubble = &quot;epi-iconBubble&quot;;
    public const string Location = &quot;epi-iconLocation&quot;;
    public const string Mail = &quot;epi-iconMail&quot;;
    public const string Telephone = &quot;epi-iconTelephone&quot;;
    public const string Website = &quot;epi-iconWebsite&quot;;
    public const string Link = &quot;epi-iconLink&quot;;
    public const string Upload = &quot;epi-iconUpload&quot;;
    public const string Error = &quot;epi-iconError&quot;;
    public const string Warning = &quot;epi-iconWarning&quot;;
    public const string Info = &quot;epi-iconInfo&quot;;
    public const string Pricing = &quot;epi-iconPricing&quot;;
    public const string Cut = &quot;epi-iconCut&quot;;
    public const string Copy = &quot;epi-iconCopy&quot;;
    public const string Paste = &quot;epi-iconPaste&quot;;
    public const string Detach = &quot;epi-iconDetach&quot;;
    public const string List = &quot;epi-iconList&quot;;
    public const string Thumbnails = &quot;epi-iconThumbnails&quot;;
    public const string Tiles = &quot;epi-iconTiles&quot;;
    public const string Project = &quot;epi-iconProject&quot;;
    public const string Published = &quot;epi-iconPublished&quot;;
    public const string PreviouslyPublished = &quot;epi-iconPreviouslyPublished&quot;;
    public const string Truck = &quot;epi-iconTruck&quot;;
    public const string Cart = &quot;epi-iconCart&quot;;
    public const string Sort = &quot;epi-iconSort&quot;;
    public const string Campaign = &quot;epi-iconCampaign&quot;;
    public const string PublishProject = &quot;epi-iconPublishProject&quot;;
    public const string Promotion = &quot;epi-iconPromotion&quot;;
    public const string Layout = &quot;epi-iconLayout&quot;;
    public const string SortAscending = &quot;epi-iconSortAscending&quot;;
    public const string SortDescending = &quot;epi-iconSortDescending&quot;;
    public const string Edited = &quot;epi-iconEdited&quot;;
    public const string Bell = &quot;epi-iconBell&quot;;
}

  &lt;/code&gt;&lt;/pre&gt;
  
  </id><updated>2016-08-31T15:51:42.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>How to get the external URL to content</title><link href="http://www.dodavinkeln.se/post/how-to-get-the-external-url-to-content" /><id>&lt;p&gt;This topic comes up from time to time in the forums. I don’t find &lt;a href=&quot;http://www.dcaric.com/blog/episerver-how-to-get-external-page-url&quot;&gt;some&lt;/a&gt; of the &lt;a href=&quot;https://andersnordby.wordpress.com/2014/04/29/getting-an-external-url-in-episerver-7-x/&quot;&gt;solutions&lt;/a&gt; given completely adequate. They usually just makes the URL absolute by appending the current context’s host name, if present. What we should do instead, is to load the &lt;code&gt;SiteDefinition&lt;/code&gt; for the content passed in to our method and then load the host from that definition. The site definition can contain multiple hosts and different kinds as well, e.g. primary host.&lt;/p&gt;  &lt;p&gt;This method also works for &lt;code&gt;MediaData&lt;/code&gt;.&lt;/p&gt;  &lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public static string ContentExternalUrl(this ContentReference contentLink, CultureInfo contentLanguage, bool absoluteUrl)
{
    var result = ServiceLocator.Current.GetInstance&amp;lt;UrlResolver&amp;gt;().GetUrl(
        contentLink,
        contentLanguage.Name,
        new VirtualPathArguments
        {
            ContextMode = ContextMode.Default,
            ForceCanonical = absoluteUrl
        });

    // HACK: Temprorary fix until GetUrl and ForceCanonical works as expected,
    // i.e returning an absolute URL even if there is a HTTP context that matches the content&#39;s site definition and host.
    if (absoluteUrl)
    {
        Uri relativeUri;

        if (Uri.TryCreate(result, UriKind.RelativeOrAbsolute, out relativeUri))
        {
            if (!relativeUri.IsAbsoluteUri)
            {
                var siteDefinitionResolver = ServiceLocator.Current.GetInstance&amp;lt;SiteDefinitionResolver&amp;gt;();
                var siteDefinition = siteDefinitionResolver.GetDefinitionForContent(contentLink, true, true);
                var hosts = siteDefinition.GetHosts(contentLanguage, true).ToList();

                var host = hosts.FirstOrDefault(h =&amp;gt; h.Type == HostDefinitionType.Primary) 
                    ?? hosts.FirstOrDefault(h =&amp;gt; h.Type == HostDefinitionType.Undefined);

                var basetUri = siteDefinition.SiteUrl;

                if (host != null &amp;amp;&amp;amp; host.Name.Equals(&amp;quot;*&amp;quot;) == false)
                {
                    // Try to create a new base URI from the host with the site&#39;s URI scheme. Name should be a valid
                    // authority, i.e. have a port number if it differs from the URI scheme&#39;s default port number.
                    Uri.TryCreate(siteDefinition.SiteUrl.Scheme + &amp;quot;://&amp;quot; + host.Name, UriKind.Absolute, out basetUri);
                }

                var absoluteUri = new Uri(basetUri, relativeUri);

                return absoluteUri.AbsoluteUri;
            }
        }
    }

    return result;
}&lt;/code&gt;&lt;/pre&gt;</id><updated>2016-01-19T14:53:19.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>PropertyFor() revisited</title><link href="http://www.dodavinkeln.se/post/propertyfor()-revisited" /><id>&lt;p&gt;Since my &lt;a href=&quot;/post/make-propertyfor()-more-consistent&quot;&gt;last post&lt;/a&gt; I’ve discovered a few issues/bugs with my approach while making rendering of properties more consistent. Unfortunately my method didn’t work that well in preview mode and Content Area properties got wrapped twice!&lt;/p&gt;  &lt;p&gt;I couldn’t find an easy way since there is no method to override when properties are rendered in preview mode (these are rendered inside &lt;code&gt;GetHtmlForEditMode()&lt;/code&gt;) and no way of telling if the property is a Content Area inside &lt;code&gt;GetHtmlForDefaultMode()&lt;/code&gt;. So instead I had to override the &lt;code&gt;PropertyFor()&lt;/code&gt; method which made the code much overly complicated and I also had to reflect some private methods from the default PropertyRenderer.&lt;/p&gt;  &lt;p&gt;Here is the new complete code:&lt;/p&gt;  &lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
///     Overrides the default renderer of all properties, so if we specify a CustomTag(Name) and optionally CssClass they get
///     rendered in view and preview mode as well. The default renderer only wraps the property in edit mode, except for
///     content areas which are always wrapped.
/// &amp;lt;/summary&amp;gt;
public class SitePropertyRenderer : PropertyRenderer
{
    private readonly CachingViewEnginesWrapper viewResolver;

    public SitePropertyRenderer(CachingViewEnginesWrapper viewResolver)
    {
        this.viewResolver = viewResolver;
    }

    public override MvcHtmlString PropertyFor&amp;lt;TModel, TValue&amp;gt;(HtmlHelper&amp;lt;TModel&amp;gt; html, string viewModelPropertyName, object additionalViewData, object editorSettings, Expression&amp;lt;Func&amp;lt;TModel, TValue&amp;gt;&amp;gt; expression, Func&amp;lt;string, MvcHtmlString&amp;gt; displayForAction)
    {
        var contextMode = html.ViewContext.RequestContext.GetContextMode();

        // Properties are always wrapped in edit mode, so no need for custom rendering
        if (contextMode == ContextMode.Edit)
        {
            return base.PropertyFor(html, viewModelPropertyName, additionalViewData, editorSettings, expression, displayForAction);
        }

        var routeValueDictionaries = new RouteValueDictionary(additionalViewData);
        var templateName = this.ResolveTemplateName(html, routeValueDictionaries, expression);
        var isContentArea = this.PropertyIsContentArea(html, expression);

        // Content areas are always wrapped, so no need for custom rendering in view mode.
        if (isContentArea)
        {
            return displayForAction(templateName);
        }

        string elementName = null;

        if (routeValueDictionaries.ContainsKey(&amp;quot;CustomTag&amp;quot;))
        {
            elementName = routeValueDictionaries[&amp;quot;CustomTag&amp;quot;] as string;
        }

        // Correctly spelled property as well, since Episerver probably made a mistake here
        if (routeValueDictionaries.ContainsKey(&amp;quot;CustomTagName&amp;quot;))
        {
            elementName = routeValueDictionaries[&amp;quot;CustomTagName&amp;quot;] as string;
        }

        string cssClass = null;

        if (routeValueDictionaries.ContainsKey(&amp;quot;CssClass&amp;quot;))
        {
            cssClass = routeValueDictionaries[&amp;quot;CssClass&amp;quot;] as string;
        }

        return this.GetHtmlForDefaultAndPreviewMode(templateName, elementName, cssClass, displayForAction);
    }

    private MvcHtmlString GetHtmlForDefaultAndPreviewMode(string templateName, string elementName, string cssClass, Func&amp;lt;string, MvcHtmlString&amp;gt; displayForAction)
    {
        // Rely on standard behavior if no element is specified
        if (string.IsNullOrEmpty(elementName))
        {
            return displayForAction(templateName);
        }

        var html = displayForAction(templateName).ToHtmlString();

        if (string.IsNullOrEmpty(html))
        {
            return MvcHtmlString.Empty;
        }

        var tag = new TagBuilder(elementName)
        {
            InnerHtml = html
        };

        if (string.IsNullOrEmpty(cssClass) == false)
        {
            tag.AddCssClass(cssClass);
        }

        return new MvcHtmlString(tag.ToString());
    }

    private bool PropertyIsContentArea&amp;lt;TModel, TValue&amp;gt;(HtmlHelper&amp;lt;TModel&amp;gt; html, Expression&amp;lt;Func&amp;lt;TModel, TValue&amp;gt;&amp;gt; expression)
    {
        var contentAreaType = typeof(ContentArea);
        var modelMetadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

        return contentAreaType.IsAssignableFrom(modelMetadata.ModelType);
    }

    #region Private methods reflected from base class

    private string ResolveTemplateName&amp;lt;TModel, TValue&amp;gt;(HtmlHelper&amp;lt;TModel&amp;gt; html, RouteValueDictionary additionalValues, Expression&amp;lt;Func&amp;lt;TModel, TValue&amp;gt;&amp;gt; expression)
    {
        var modelMetadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

        var tag = additionalValues[&amp;quot;tag&amp;quot;] as string;

        if (string.IsNullOrEmpty(tag) &amp;amp;&amp;amp; modelMetadata != null)
        {
            tag = this.GetTagFromModelMetadata(modelMetadata);
        }

        if (string.IsNullOrEmpty(tag) == false &amp;amp;&amp;amp; modelMetadata != null)
        {
            var templateResolver = html.ViewData[&amp;quot;templateResolver&amp;quot;] as TemplateResolver ?? ServiceLocator.Current.GetInstance&amp;lt;TemplateResolver&amp;gt;();

            var templateModel = templateResolver.Resolve(
                html.ViewContext.HttpContext,
                modelMetadata.ModelType,
                modelMetadata.Model,
                TemplateTypeCategories.MvcPartialView,
                tag);

            var templateName = this.GetTemplateName(templateModel, html.ViewContext);

            if (string.IsNullOrEmpty(templateName) == false)
            {
                return templateName;
            }
        }

        if (this.DisplayTemplateWithNameExists(html.ViewContext, tag) == false)
        {
            return null;
        }

        return tag;
    }

    private string GetTagFromModelMetadata(ModelMetadata metaData)
    {
        if (metaData == null || metaData.ContainerType == null)
        {
            return null;
        }

        var property = metaData.ContainerType.GetProperty(metaData.PropertyName);

        if (property != null)
        {
            var uIHintAttributes = property.GetCustomAttributes(true).OfType&amp;lt;UIHintAttribute&amp;gt;();

            var uIHintAttribute = uIHintAttributes.FirstOrDefault(a =&amp;gt; string.Equals(a.PresentationLayer, &amp;quot;website&amp;quot;, StringComparison.OrdinalIgnoreCase));

            if (uIHintAttribute != null)
            {
                return uIHintAttribute.UIHint;
            }

            uIHintAttribute = uIHintAttributes.FirstOrDefault(a =&amp;gt; string.IsNullOrEmpty(a.PresentationLayer));

            if (uIHintAttribute != null)
            {
                return uIHintAttribute.UIHint;
            }
        }

        return null;
    }

    private string GetTemplateName(TemplateModel templateModel, ControllerContext viewContext)
    {
        if (templateModel == null)
        {
            return null;
        }

        if (this.DisplayTemplateWithNameExists(viewContext, templateModel.Name) == false)
        {
            return null;
        }

        return templateModel.Name;
    }

    private bool DisplayTemplateWithNameExists(ControllerContext viewContext, string templateName)
    {
        if (string.IsNullOrEmpty(templateName))
        {
            return false;
        }

        var viewEngineResult = this.viewResolver.FindPartialView(viewContext, $&amp;quot;DisplayTemplates/{templateName}&amp;quot;);

        if (viewEngineResult == null)
        {
            return false;
        }

        return viewEngineResult.View != null;
    }

    #endregion
}&lt;/code&gt;&lt;/pre&gt;</id><updated>2016-01-13T21:08:43.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Make PropertyFor() more consistent</title><link href="http://www.dodavinkeln.se/post/make-propertyfor()-more-consistent" /><id>&lt;p&gt;Coming from WebForms development I was a bit annoyed with the inconsistent behavior of the &lt;code&gt;PropertyFor()&lt;/code&gt; Html Helper. In WebForms properties are always wrapped with an element, but in MVC this is only true in edit mode. Please see &lt;a href=&quot;http://joelabrahamsson.com/how-episervers-html-helper-propertyfor-works/&quot;&gt;Joel’s excelent post about &lt;code&gt;PropertyFor()&lt;/code&gt;&lt;/a&gt; and what gets actually rendered to the client.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt; Properties always gets wrapped with an element in edit mode, but never in view mode. However this is only true for non ContentArea properties, which are not really the problem here.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Why is this a problem you might ask? Well this might actually break your design, especially in edit mode where the extra wrapping element might add styling that you don’t want. So how can we fix this?&lt;/p&gt;  &lt;p&gt;One solution is to not use &lt;code&gt;PropertyFor()&lt;/code&gt; at all and instead use &lt;code&gt;DisplayFor()&lt;/code&gt; or just &lt;code&gt;@Model.YourProperty&lt;/code&gt;. But then you need to add the editing attributes manually to a parent element but then that parent will always get rendered in view mode, even though the property doesn’t have a value. You can fix that by adding an if-statement around the parent and the property and then check whether we are in edit mode and/or the property has a value (we always need to render the parent in edit mode). Not so nice, and a lot of unnecessary code and logic in the view:&lt;/p&gt;  &lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;@if (EPiServer.Editor.PageEditing.PageIsInEditMode || Model.CurrentPage.PageHeading != null)
{
	&amp;lt;h2 @Html.EditAttributes(x =&amp;gt; x.CurrentPage.PageHeading)&amp;gt;
		@Html.DisplayFor(x =&amp;gt; x.CurrentPage.PageHeading)
	&amp;lt;/h2&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Replacing the &lt;code&gt;PropertyRenderer&lt;/code&gt; was the easist way I found to fix this behavior. Please let me know in the comment section if you have a better way of solving this! It was actually really easy:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;
public class SitePropertyRenderer : PropertyRenderer
{
    protected override MvcHtmlString GetHtmlForDefaultMode&amp;lt;TModel, TValue&amp;gt;(string propertyName, string templateName, string elementName, string elementCssClass, Func&amp;lt;string, MvcHtmlString&amp;gt; displayForAction)
    {
        if (string.IsNullOrEmpty(elementName))
        {
            return displayForAction(templateName);
        }

        var html = displayForAction(templateName).ToHtmlString();

        if (string.IsNullOrEmpty(html))
        {
            return MvcHtmlString.Empty;
        }

        var tag = new TagBuilder(elementName)
        {
            InnerHtml = html
        };

        if (string.IsNullOrEmpty(elementCssClass) == false)
        {
            tag.AddCssClass(elementCssClass);
        }

        return new MvcHtmlString(tag.ToString());
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We only have to override&amp;#160; how the property is rendered in view mode and always add the wrapping element. We also need to replace the default renderer in the container with our own, which can be done in an initializable module:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[InitializableModule]
public class ContainerConfigurableModule : IConfigurableModule
{
    public void ConfigureContainer(ServiceConfigurationContext context)
    {
        context.Container.Configure(container =&amp;gt;
            container.For&amp;lt;PropertyRenderer&amp;gt;().Use&amp;lt;SitePropertyRenderer&amp;gt;());
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we can write like this in the view instead, and the wrapper gets always rendered, but only in view mode if the property has a value:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;@Html.PropertyFor(x =&amp;gt; x.CurrentPage.PageHeading, new { CustomTag = &amp;quot;h2&amp;quot; })&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will probably not be an issue in MVC 6, hopefully in a near future, where we can use &lt;a href=&quot;https://github.com/aspnet/Mvc/tree/dev/src/Microsoft.AspNet.Mvc.TagHelpers&quot;&gt;Tag Helpers&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;I have some more blog post in the pipeline on the same topic; keeping the views and output clean. So stay tuned!&lt;/p&gt;</id><updated>2016-01-07T19:47:00.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Generate a download link to a file</title><link href="http://www.dodavinkeln.se/post/generate-a-download-link-to-a-file" /><id>&lt;p&gt;This is somewhat of a hidden feature in EPiServer. But there is actually a built-in feature to send a download dialog to the user instead of opening the file directly. Soon the download attribute, that you can set on anchors, will work in all browsers but in the meanwhile we can do:&lt;/p&gt;  &lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public static string GetDownloadLink(this ContentReference contentReferenceToMediaFile)
{
    var url = UrlResolver.Current.GetUrl(contentReferenceToMediaFile);

    return url + &amp;quot;/download&amp;quot;;
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The important part here is of course &lt;code&gt;/download&lt;/code&gt;. For images you can add &lt;code&gt;/thumbnail&lt;/code&gt; (or the name of another blob property) to get the thumbnail of the image.&lt;/p&gt;</id><updated>2015-09-23T15:19:19.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>How to inject a script for just one block?</title><link href="http://dodavinkeln.se/post/how-to-inject-a-script-for-a-block" /><id>&lt;p&gt;The answer is very simple, but first your template(s) must meet these &lt;a href=&quot;/link/082e2f78d59d4dd1ad02358aac4b379c.aspx&quot;&gt;client resource requirements&lt;/a&gt;. The article mention how to register a script on a page level, and in this case it will be injected on ALL templates that meet the requirements, i.e. page type name is XFormPage.&lt;/p&gt;  &lt;p&gt;But let’s say you just want to inject a script if a specific block is used on the page. One solution is to use the method in the article and then iterate through all blocks in the page’s content areas and see if the block is used. Probably not the smartest and fastest solution.&lt;/p&gt;  &lt;p&gt;A more simple solution is to require the script in the block’s view:&lt;/p&gt;  &lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;var clientResources = ServiceLocator.Current.GetInstance&amp;lt;IRequiredClientResourceList&amp;gt;();

clientResources.RequireScript(&amp;quot;/scripts/my-block-script.js&amp;quot;, &amp;quot;MyScript&amp;quot;, new[] { &amp;quot;MainScripts&amp;quot; }).AtFooter();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If the script doesn&#39;t have any dependencies, you can pass &lt;code&gt;Enumerable.Empty&amp;lt;string&amp;gt;()&lt;/code&gt; instead of &lt;code&gt;new[] { &amp;quot;MainScripts&amp;quot; }&lt;/code&gt;.&lt;/p&gt;</id><updated>2015-02-04T20:54:59.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>How to inject a script for just one block?</title><link href="http://www.dodavinkeln.se/post/how-to-inject-a-script-for-a-block" /><id>&lt;p&gt;The answer is very simple, but first your template(s) must meet these &lt;a href=&quot;/link/082e2f78d59d4dd1ad02358aac4b379c.aspx&quot;&gt;client resource requirements&lt;/a&gt;. The article mention how to register a script on a page level, and in this case it will be injected on ALL templates that meet the requirements, i.e. page type name is XFormPage.&lt;/p&gt;&lt;p&gt;But let’s say you just want to inject a script if a specific block is used on the page. One solution is to use the method in the article and then iterate through all blocks in the page’s content areas and see if the block is used. Probably not the smartest and fastest solution.&lt;/p&gt;&lt;p&gt;A more simple solution is to require the script in the block’s view or in your controller:&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;var clientResources = ServiceLocator.Current.GetInstance&amp;lt;IRequiredClientResourceList&amp;gt;();

clientResources.RequireScript(&quot;/scripts/my-block-script.js&quot;, &quot;MyScript&quot;, new[] { &quot;MainScripts&quot; }).AtFooter();&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If the script doesn&#39;t have any dependencies, you can pass &lt;code&gt;Enumerable.Empty&amp;lt;string&amp;gt;()&lt;/code&gt; instead of &lt;code&gt;new[] { &quot;MainScripts&quot; }&lt;/code&gt;.&lt;/p&gt;</id><updated>2015-02-04T20:54:59.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Today’s gotcha: Make sure if you really want to retrieve content with a WorkID!</title><link href="http://dodavinkeln.se/post/todays-gotcha-make-sure-if-you-really-want-to-retrieve-content-with-a-workid" /><id>&lt;p&gt;So, I was struggling with creating and updating content in different languages from code. I was able to create language versions of every content for all the languages, but they all ended up with the same content – the content of the first language version, i.e. the master language!&lt;/p&gt;  &lt;p&gt;Turned out I was calling &lt;code&gt;Get&amp;lt;T&amp;gt;(ContentReference contentLink, ILanguageSelector selector)&lt;/code&gt; in &lt;code&gt;IContentLoader&lt;/code&gt; with a &lt;code&gt;ContentReference&lt;/code&gt; that had a &lt;var&gt;WorkID&lt;/var&gt; to get the current data for updating it. That did of course override the &lt;var&gt;selector&lt;/var&gt; parameter I was passing with another language. The content I got back was in the language of the content with that specific WorkID (in this case the master language). So my code just kept updating the master language version and not the language version I was trying to update.&lt;/p&gt;  &lt;p&gt;Bottom line; Call &lt;code&gt;Get()&lt;/code&gt; with a &lt;code&gt;ContentReference&lt;/code&gt; without a &lt;var&gt;WorkID&lt;/var&gt;, unless you really want to get that specific version of the content. You can call the extension method &lt;code&gt;ToReferenceWithoutVersion()&lt;/code&gt; to construct a &lt;code&gt;ContentReference&lt;/code&gt; without a &lt;var&gt;WorkID&lt;/var&gt;.&lt;/p&gt;</id><updated>2015-01-09T07:10:41.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Today’s gotcha: Make sure if you really want to retrieve content with a WorkID!</title><link href="http://www.dodavinkeln.se/post/todays-gotcha-make-sure-if-you-really-want-to-retrieve-content-with-a-workid" /><id>&lt;p&gt;So, I was struggling with creating and updating content in different languages from code. I was able to create language versions of every content for all the languages, but they all ended up with the same content – the content of the first language version, i.e. the master language!&lt;/p&gt;  &lt;p&gt;Turned out I was calling &lt;code&gt;Get&amp;lt;T&amp;gt;(ContentReference contentLink, ILanguageSelector selector)&lt;/code&gt; in &lt;code&gt;IContentLoader&lt;/code&gt; with a &lt;code&gt;ContentReference&lt;/code&gt; that had a &lt;var&gt;WorkID&lt;/var&gt; to get the current data for updating it. That did of course override the &lt;var&gt;selector&lt;/var&gt; parameter I was passing with another language. The content I got back was in the language of the content with that specific WorkID (in this case the master language). So my code just kept updating the master language version and not the language version I was trying to update.&lt;/p&gt;  &lt;p&gt;Bottom line; Call &lt;code&gt;Get()&lt;/code&gt; with a &lt;code&gt;ContentReference&lt;/code&gt; without a &lt;var&gt;WorkID&lt;/var&gt;, unless you really want to get that specific version of the content. You can call the extension method &lt;code&gt;ToReferenceWithoutVersion()&lt;/code&gt; to construct a &lt;code&gt;ContentReference&lt;/code&gt; without a &lt;var&gt;WorkID&lt;/var&gt;.&lt;/p&gt;</id><updated>2015-01-09T07:10:41.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Today’s gotcha: Make sure you really want to retrieve content with a WorkID!</title><link href="http://dodavinkeln.se/post/todays-gotcha-make-sure-you-really-want-to-retrieve-content-with-a-workid" /><id>&lt;p&gt;So, I was struggling with creating and updating content in different languages from code. I was able to create language versions of every content for all the languages, but they all ended up with the same content – the content of the first language version, i.e. the master language!&lt;/p&gt;  &lt;p&gt;Turned out I was calling &lt;code&gt;Get&amp;lt;T&amp;gt;(ContentReference contentLink, ILanguageSelector selector)&lt;/code&gt; in &lt;code&gt;IContentLoader&lt;/code&gt; with a &lt;code&gt;ContentReference&lt;/code&gt; that had a &lt;var&gt;WorkID&lt;/var&gt; to get the current data for updating it. That did of course override the &lt;var&gt;selector&lt;/var&gt; parameter I was passing with another language. The content I got back was in the language of the content with that specific WorkID (in this case the master language). So my code just kept updating the master language version and not the language version I was trying to update.&lt;/p&gt;  &lt;p&gt;Bottom line; Call &lt;code&gt;Get()&lt;/code&gt; with a &lt;code&gt;ContentReference&lt;/code&gt; without a &lt;var&gt;WorkID&lt;/var&gt;, unless you really want to get that specific version of the content. You can call the extension method &lt;code&gt;ToReferenceWithoutVersion()&lt;/code&gt; to construct a &lt;code&gt;ContentReference&lt;/code&gt; without a &lt;var&gt;WorkID&lt;/var&gt;.&lt;/p&gt;</id><updated>2015-01-09T02:10:21.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Add support for container pages by marking content types with an interface</title><link href="http://www.dodavinkeln.se/post/add-support-for-container-pages-by-marking-content-types-with-an-interface" /><id>&lt;p&gt;First of all, &lt;a href=&quot;http://tech-fellow.net/2014/01/31/add-support-container-pages-episerver-7-5/&quot;&gt;this is not a new concept&lt;/a&gt; but the API has changed since EPiServer 7.14. IUIDescriptorInitializer is now obsolete. So here’s my take on this.&lt;/p&gt;  &lt;p&gt;First we need an interface to “mark” the content types with: &lt;/p&gt;  &lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;namespace DV
{
    /// &amp;lt;summary&amp;gt;
    /// Interface to mark content types as container pages, i.e. changing the icon
    /// in the page tree and defaulting to All Properties view.
    /// &amp;lt;/summary&amp;gt;
    public interface IContainerPage
    {
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then we need a function to get all types that is marked with this interface, this is a &lt;a href=&quot;http://tech-fellow.net/2014/01/31/add-support-container-pages-episerver-7-5/&quot;&gt;straight copy of Valdis code&lt;/a&gt; but I wanted to include it in my post as well to avoid a possible dead link in the future:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;namespace DV
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;

    /// &amp;lt;summary&amp;gt;
    /// Helper functions for working with types.
    /// &amp;lt;/summary&amp;gt;
    public static class TypeFunctions
    {
        /// &amp;lt;summary&amp;gt;
        /// Get all types that implements or is a subclass of the specified type.
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;typeparam name=&amp;quot;T&amp;quot;&amp;gt;The type of the superclass.&amp;lt;/typeparam&amp;gt;
        /// &amp;lt;returns&amp;gt;Returns a list of types that implements or is a subclass of the specified type.&amp;lt;/returns&amp;gt;
        public static IEnumerable&amp;lt;Type&amp;gt; GetTypesChildOf&amp;lt;T&amp;gt;()
        {
            var types = new List&amp;lt;Type&amp;gt;();

            foreach (var assembly in TypeFunctions.GetAssemblies())
            {
                types.AddRange(TypeFunctions.GetTypesChildOfInAssembly(typeof(T), assembly));
            }

            return types;
        }

        private static IEnumerable&amp;lt;Assembly&amp;gt; GetAssemblies()
        {
            return AppDomain.CurrentDomain.GetAssemblies();
        }

        private static IEnumerable&amp;lt;Type&amp;gt; GetTypesChildOfInAssembly(Type type, Assembly assembly)
        {
            try
            {
                return assembly.GetTypes().Where(t =&amp;gt; type.IsAssignableFrom(t) &amp;amp;&amp;amp; t.IsClass);
            }
            catch (Exception)
            {
                // there could be situations when type could not be loaded
                // this may happen if we are visiting *all* loaded assemblies in application domain
                return Enumerable.Empty&amp;lt;Type&amp;gt;();
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then we only need an initializable module that iterates through all UI descriptors, finds the ones that are marked with the IContainerPage interface and then finally set the default view and icon class.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;namespace DV
{
    using System;
    using System.Linq;
    using EPiServer.Framework;
    using EPiServer.Framework.Initialization;
    using EPiServer.Shell;
    using EPiServer.ServiceLocation;

    [InitializableModule]
    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class ContainerPageUIDescriptorInitialization : IInitializableModule
    {
        public void Initialize(InitializationEngine context)
        {
            var containerTypes = TypeFunctions.GetTypesChildOf&amp;lt;IContainerPage&amp;gt;();
            var registry = ServiceLocator.Current.GetInstance&amp;lt;UIDescriptorRegistry&amp;gt;();
            var containerDescriptors = registry.UIDescriptors.Where(d =&amp;gt; containerTypes.Contains(d.ForType));

            foreach (var descriptor in containerDescriptors)
            {
                descriptor.DefaultView = CmsViewNames.AllPropertiesView;
                descriptor.IconClass = ContentTypeCssClassNames.Container;
            }
        }

        public void Preload(string[] parameters) { }

        public void Uninitialize(InitializationEngine context)
        {
            // Do nothing.
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</id><updated>2014-10-22T00:59:02.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>How to create an editable 404 page</title><link href="http://www.dodavinkeln.se/post/how-to-create-an-editable-404-page" /><id>&lt;p&gt;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?&lt;/p&gt;  &lt;p&gt;First we need a model for the 404 page:&lt;/p&gt;  &lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;namespace DV.Models
{
    using EPiServer.Core;
    using EPiServer.DataAbstraction;
    using EPiServer.DataAnnotations;

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

            this.VisibleInMenu = false; 
        }
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then we need a template for the page and more importantly, we need to set the status code to 404:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;namespace DV.Views
{
    using EPiServer;
    using EPiServer.Editor;
    using log4net;
    using System;
    using DV.Models;

    public partial class NotFound : TemplatePage&amp;lt;NotFoundPage&amp;gt;
    {
        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 &amp;amp;&amp;amp; Request.HttpMethod == &amp;quot;GET&amp;quot;)
            {
                Log.InfoFormat(&amp;quot;404 {0}&amp;quot;, Request.QueryString[&amp;quot;aspxerrorpath&amp;quot;] ?? Request.RawUrl);

                this.Response.Status = &amp;quot;404 not found&amp;quot;;
                this.Response.StatusCode = 404;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;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 &lt;strong&gt;Request.RawUrl&lt;/strong&gt; will contain the actual requested url!&lt;/p&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;lt;system.webServer&amp;gt;
	&amp;lt;httpErrors errorMode=&amp;quot;Custom&amp;quot;&amp;gt;
		&amp;lt;remove statusCode=&amp;quot;404&amp;quot; /&amp;gt;
		&amp;lt;error statusCode=&amp;quot;404&amp;quot; path=&amp;quot;/Views/NotFound.aspx&amp;quot; responseMode=&amp;quot;ExecuteURL&amp;quot; /&amp;gt;
	&amp;lt;/httpErrors&amp;gt;
&amp;lt;/system.webServer&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;lt;episerver&amp;gt;
	&amp;lt;applicationSettings globalErrorHandling=&amp;quot;Off&amp;quot; /&amp;gt;
&amp;lt;/episerver&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Best practice is to also to configure customErrors inside system.Web:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;lt;system.web&amp;gt;
	&amp;lt;customErrors defaultRedirect=&amp;quot;/Views/NotFound.aspx&amp;quot; mode=&amp;quot;Off&amp;quot; redirectMode=&amp;quot;ResponseRewrite&amp;quot;&amp;gt;
		&amp;lt;error statusCode=&amp;quot;404&amp;quot; redirect=&amp;quot;/Views/NotFound.aspx&amp;quot; /&amp;gt;
	&amp;lt;/customErrors&amp;gt;
&amp;lt;/system.web&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[AllowedTypes(new[] { typeof(NotFoundPage) })]
public virtual PageReference NotFoundPage { get; set; }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And in the code-behind of our 404 template we just load that page in the OnPreInit event:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;protected override void OnPreInit(EventArgs e)
{
    base.OnPreInit(e);

    var contentLoader = ServiceLocator.Current.GetInstance&amp;lt;IContentLoader&amp;gt;();
    var startPage = contentLoader.Get&amp;lt;StartPage&amp;gt;(ContentReference.StartPage);

    if (!PageEditing.PageIsInEditMode &amp;amp;&amp;amp; !ContentReference.IsNullOrEmpty(startPage.NotFoundPage))
    {
        this.CurrentPageLink = startPage.NotFoundPage;
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;protected override void InitializeCulture()
{
    if (!PageEditing.PageIsInEditMode)
    {
        // Try to find the language id from the actual requested url
        var segments = Request.RawUrl.Split(&#39;/&#39;);
        var languageSegment = segments.Length &amp;gt; 1 ? segments[1] : string.Empty;

        if (!string.IsNullOrWhiteSpace(languageSegment))
        {
            var languageMatcher = ServiceLocator.Current.GetInstance&amp;lt;ILanguageSegmentMatcher&amp;gt;();

            var languageId = string.Empty;

            if (languageMatcher.TryGetLanguageId(languageSegment, out languageId))
            {
                var currentLanguage = ServiceLocator.Current.GetInstance&amp;lt;IUpdateCurrentLanguage&amp;gt;();

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

    base.InitializeCulture();
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;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.&lt;/p&gt;</id><updated>2014-08-28T18:33:14.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Wrap properties with extra markup</title><link href="http://www.dodavinkeln.se/post/wrap-properties-with-extra-markup" /><id>&lt;p&gt;I’m still on my quest for clean markup and a nice editor experience. Please see my previous &lt;a href=&quot;/post/how-to-handle-links-in-blocks&quot;&gt;post how about to handle wrapping links&lt;/a&gt;. 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.&lt;/p&gt;  &lt;p&gt;I don’t think there is much to say about the code, it’s very self-explanatory, so here it comes:&lt;/p&gt;  &lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;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
    }

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

        /// &amp;lt;summary&amp;gt;
        ///     Gets or sets the type of the evaluation.
        /// &amp;lt;/summary&amp;gt;
        public EvaluationType EvaluationType { get; set; }

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

        // ReSharper disable once UnusedAutoPropertyAccessor.Local
        private Injected&amp;lt;ControlRenderContextBuilder&amp;gt; ContextBuilder { get; set; }

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

            if (this.Visible &amp;amp;&amp;amp; 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 &amp;amp;&amp;amp; property.IsNull == false;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Here are some examples on how we can use the control&lt;/h2&gt;

&lt;p&gt;In this first example we want to wrap a content area with teaser blocks:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;lt;DV:PropertyPlaceHolder PropertyName=&amp;quot;Teasers&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
	&amp;lt;div class=&amp;quot;teaser-listing&amp;quot;&amp;gt;
		&amp;lt;div class=&amp;quot;wrapper&amp;quot;&amp;gt;
			&amp;lt;EPiServer:Property PropertyName=&amp;quot;Teasers&amp;quot; CssClass=&amp;quot;m-cols-2&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
				&amp;lt;RenderSettings ChildrenCssClass=&amp;quot;m-col&amp;quot; /&amp;gt;
			&amp;lt;/EPiServer:Property&amp;gt;
		&amp;lt;/div&amp;gt;
	&amp;lt;/div&amp;gt;
&amp;lt;/DV:PropertyPlaceHolder&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;lt;DV:PropertyPlaceHolder PropertyName=&amp;quot;Department,Office,Email,Telephone&amp;quot; EvaluationType=&amp;quot;Any&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
	&amp;lt;dl&amp;gt;
		&amp;lt;DV:PropertyPlaceHolder PropertyName=&amp;quot;Department&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
			&amp;lt;dt&amp;gt;Department&amp;lt;/dt&amp;gt;
			&amp;lt;EPiServer:Property PropertyName=&amp;quot;Department&amp;quot; CustomTagName=&amp;quot;dd&amp;quot; runat=&amp;quot;server&amp;quot; /&amp;gt;
		&amp;lt;/DV:PropertyPlaceHolder&amp;gt;

		&amp;lt;DV:PropertyPlaceHolder PropertyName=&amp;quot;Office&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
			&amp;lt;dt&amp;gt;Office&amp;lt;/dt&amp;gt;
			&amp;lt;EPiServer:Property PropertyName=&amp;quot;Office&amp;quot; CustomTagName=&amp;quot;dd&amp;quot; runat=&amp;quot;server&amp;quot; /&amp;gt;
		&amp;lt;/DV:PropertyPlaceHolder&amp;gt;

		&amp;lt;DV:PropertyPlaceHolder PropertyName=&amp;quot;Email&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
			&amp;lt;dt&amp;gt;Email&amp;lt;/dt&amp;gt;
			&amp;lt;EPiServer:Property PropertyName=&amp;quot;Email&amp;quot; CustomTagName=&amp;quot;dd&amp;quot; runat=&amp;quot;server&amp;quot; /&amp;gt;
		&amp;lt;/DV:PropertyPlaceHolder&amp;gt;

		&amp;lt;DV:PropertyPlaceHolder PropertyName=&amp;quot;Telephone&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
			&amp;lt;dt&amp;gt;Telephone&amp;lt;/dt&amp;gt;
			&amp;lt;EPiServer:Property PropertyName=&amp;quot;Telephone&amp;quot; CustomTagName=&amp;quot;dd&amp;quot; runat=&amp;quot;server&amp;quot; /&amp;gt;
		&amp;lt;/DV:PropertyPlaceHolder&amp;gt;
	&amp;lt;/dl&amp;gt;
&amp;lt;/DV:PropertyPlaceHolder&amp;gt;&lt;/code&gt;&lt;/pre&gt;</id><updated>2014-07-06T04:09:25.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Test 1</title><link href="http://www.dodavinkeln.se/post/test-1" /><id>this is a test</id><updated>2014-06-02T11:58:00.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>How to handle links in blocks and pages, that should wrap content</title><link href="http://www.dodavinkeln.se/post/how-to-handle-links-in-blocks" /><id>&lt;p&gt;First off, there is a similar solution already in the Alloy templates. But this one is a bit more refined.&lt;/p&gt;  &lt;p&gt;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:&lt;/p&gt;  &lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;lt;div class=&amp;quot;teaser&amp;quot;&amp;gt;
	&amp;lt;div class=&amp;quot;image-wrapper&amp;quot;&amp;gt;
		&amp;lt;img src=&amp;quot;/image.jpg&amp;quot; alt=&amp;quot;&amp;quot; /&amp;gt;
	&amp;lt;/div&amp;gt;
	&amp;lt;a class=&amp;quot;content-wrapper&amp;quot; href=&amp;quot;/about-us/&amp;quot;&amp;gt;
		&amp;lt;p&amp;gt;Tagline&amp;lt;/p&amp;gt;
		&amp;lt;h2&amp;gt;Heading&amp;lt;/h2&amp;gt;
	&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And without a link:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;lt;div class=&amp;quot;teaser&amp;quot;&amp;gt;
	&amp;lt;div class=&amp;quot;image-wrapper&amp;quot;&amp;gt;
		&amp;lt;img src=&amp;quot;/image.jpg&amp;quot; alt=&amp;quot;&amp;quot; /&amp;gt;
	&amp;lt;/div&amp;gt;
	&amp;lt;div class=&amp;quot;content-wrapper&amp;quot;&amp;gt;
		&amp;lt;p&amp;gt;Tagline&amp;lt;/p&amp;gt;
		&amp;lt;h2&amp;gt;Heading&amp;lt;/h2&amp;gt;
	&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;lt;div class=&amp;quot;teaser&amp;quot;&amp;gt;
	&amp;lt;div class=&amp;quot;image-wrapper&amp;quot;&amp;gt;
		&amp;lt;EPiServer:Property runat=&amp;quot;server&amp;quot; PropertyName=&amp;quot;Image&amp;quot; /&amp;gt;
	&amp;lt;/div&amp;gt;
	&amp;lt;asp:PlaceHolder runat=&amp;quot;server&amp;quot; Visible=&amp;quot;&amp;lt;%# CurrentBlock.Link != null &amp;amp;&amp;amp; PageEditing.PageIsInEditMode == false %&amp;gt;&amp;quot;&amp;gt;
		&amp;lt;a class=&amp;quot;content-wrapper&amp;quot; href=&amp;quot;&amp;lt;%= CurrentBlock.Link  %&amp;gt;&amp;quot;&amp;gt;
	&amp;lt;/asp:PlaceHolder&amp;gt;

	&amp;lt;asp:PlaceHolder runat=&amp;quot;server&amp;quot; Visible=&amp;quot;&amp;lt;%# CurrentBlock.Link == null || PageEditing.PageIsInEditMode %&amp;gt;&amp;quot;&amp;gt;
		&amp;lt;div class=&amp;quot;content-wrapper&amp;quot;&amp;gt;
	&amp;lt;/asp:PlaceHolder&amp;gt;

		&amp;lt;EPiServer:Property runat=&amp;quot;server&amp;quot; PropertyName=&amp;quot;Tagline&amp;quot; CustomTagName=&amp;quot;p&amp;quot; /&amp;gt;
		&amp;lt;EPiServer:Property runat=&amp;quot;server&amp;quot; PropertyName=&amp;quot;Heading&amp;quot; CustomTagName=&amp;quot;h2&amp;quot; /&amp;gt;

	&amp;lt;asp:PlaceHolder runat=&amp;quot;server&amp;quot; Visible=&amp;quot;&amp;lt;%# CurrentBlock.Link == null || PageEditing.PageIsInEditMode %&amp;gt;&amp;quot;&amp;gt;
		&amp;lt;/div&amp;gt;
	&amp;lt;/asp:PlaceHolder&amp;gt;

	&amp;lt;asp:PlaceHolder runat=&amp;quot;server&amp;quot; Visible=&amp;quot;&amp;lt;%# CurrentBlock.Link != null &amp;amp;&amp;amp; PageEditing.PageIsInEditMode == false %&amp;gt;&amp;quot;&amp;gt;
		&amp;lt;/a&amp;gt;
	&amp;lt;/asp:PlaceHolder&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;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 &lt;strong&gt;CustomTagName&lt;/strong&gt;, 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.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;namespace DV.Controls
{
    using EPiServer;
    using EPiServer.Editor;
    using System;
    using System.Web.UI;
    using System.Web.UI.WebControls;

    /// &amp;lt;summary&amp;gt;
    /// 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.
    /// &amp;lt;/summary&amp;gt;
    [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 &amp;amp;&amp;amp;
                this.TagKey == HtmlTextWriterTag.Unknown)
            {
                return;
            }

            base.RenderBeginTag(writer);
        }

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

            base.RenderEndTag(writer);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we can write like this instead:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;lt;div class=&amp;quot;teaser&amp;quot;&amp;gt;
	&amp;lt;div class=&amp;quot;image-wrapper&amp;quot;&amp;gt;
		&amp;lt;EPiServer:Property runat=&amp;quot;server&amp;quot; PropertyName=&amp;quot;Image&amp;quot; /&amp;gt;
	&amp;lt;/div&amp;gt;
	&amp;lt;DV:ToggleLink Url=&amp;quot;&amp;lt;%# CurrentBlock.Link %&amp;gt;&amp;quot; CustomTagName=&amp;quot;div&amp;quot; CssClass=&amp;quot;content-wrapper&amp;quot; runat=&amp;quot;server&amp;quot;&amp;gt;
		&amp;lt;EPiServer:Property runat=&amp;quot;server&amp;quot; PropertyName=&amp;quot;Tagline&amp;quot; CustomTagName=&amp;quot;p&amp;quot; /&amp;gt;
		&amp;lt;EPiServer:Property runat=&amp;quot;server&amp;quot; PropertyName=&amp;quot;Heading&amp;quot; CustomTagName=&amp;quot;h2&amp;quot; /&amp;gt;
	&amp;lt;/DV:ToggleLink&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Much nicer code, if you ask me.&lt;/p&gt;</id><updated>2014-02-27T06:25:12.0000000Z</updated><summary type="html">Blog post</summary></entry></feed>