<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by eGandalf</title><link href="http://world.optimizely.com" /><updated>2016-10-11T15:44:11.0000000Z</updated><id>https://world.optimizely.com/blogs/egandalf/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>Rendering a ContentFolder as a Block to List Assets</title><link href="https://world.optimizely.com/blogs/egandalf/dates/2016/10/rendering-a-contentfolder-as-a-block-to-list-assets/" /><id>&lt;p&gt;Update: I&#39;m an idiot. Fixed it up.&lt;/p&gt;
&lt;p&gt;For content-heavy clients, we get the occasional request for listing file assets on the site for download. From what I can tell, there are a couple of options that require a bit of author effort, but I really wanted to provide something that is more suited to the best authoring experience offered by Episerver - drag and drop within Content Areas.&lt;/p&gt;
&lt;p&gt;Knowing I can drag and drop most content types into a ContentArea and expect something to happen, I decided to give it a try using one of the asset panel ContentFolders. So I hover my mouse over a folder, casually hold down the mouse button, and drag the folder into an open area of the page. The field highlights! The folder drops in! It works!&lt;/p&gt;
&lt;p&gt;Almost.&lt;/p&gt;
&lt;p&gt;According to Episerver&#39;s documentation, &quot;a&lt;span&gt;&amp;nbsp;content folder is used to structure content and has no visual appearance on the site.&quot;&amp;nbsp;&lt;/span&gt;Meaning that the ContentFolder type has no Controller and no View.&lt;/p&gt;
&lt;p&gt;Great. I can fix that. Code below.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Here&#39;s the part where I was an idiot. I was using the wrong parameter name in my Index method and therefore thought that the system was always returning null. Key lesson: make sure you&#39;re aligning with convention. So I deleted where I was explaining my workaround that I didn&#39;t need and am just including the final code below. This isn&#39;t production, but feel free to point out my other mistakes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Controller:&lt;/p&gt;
&lt;pre&gt;using EPiServer.Core;
using EPiServer.Web.Mvc;
using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using EPiServer;
using EPiServer.ServiceLocation;
using Alloy.Models.ViewModels;
using Alloy.Models.Media;
using Alloy.Business;

namespace Alloy.Controllers
{
    public class ContentFolderController : PartialContentController
    {
        private IContentRepository contentRepository =&amp;gt; ServiceLocator.Current.GetInstance();
        // GET: ContentFolder
        public override ActionResult Index(ContentFolder currentContent)
        {
            if(currentContent != null)
            {
                var model = new ContentFolderViewModel();
                model.Name = currentContent.Name;

                var documents = contentRepository.GetChildren(currentContent.ContentLink);

                model.Documents = documents.Select(d =&amp;gt; new DocumentViewModel() {
                    Name = d.Name,
                    Description = d.Description,
                    Url = d.GetUrl(),
                    FileSize = d.BinaryData.GetSize(),
                    EveryoneHasAccess = d.IsAvailableToEveryone()
                });

                return PartialView(model);
            }
            return PartialView();
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;View:&lt;/p&gt;
&lt;pre&gt;@model Alloy.Models.ViewModels.ContentFolderViewModel

&amp;lt;h3&amp;gt;@Model.Name&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
@foreach (var doc in Model.Documents)
{
    &amp;lt;li&amp;gt;
        &amp;lt;a href=&quot;@doc.Url&quot;&amp;gt;@doc.Name&amp;lt;/a&amp;gt;
        @if (!string.IsNullOrWhiteSpace(doc.Description))
        { &amp;lt;div&amp;gt;@Html.Raw(doc.Description)&amp;lt;/div&amp;gt; }
        &amp;lt;div style=&quot;font-style: italic; color: #777&quot;&amp;gt;File size: @doc.FileSize&amp;lt;/div&amp;gt;
    &amp;lt;/li&amp;gt;
}
&amp;lt;/ul&amp;gt;
&lt;/pre&gt;
&lt;p&gt;ViewModels:&lt;/p&gt;
&lt;pre&gt;using System.Collections.Generic;

namespace Alloy.Models.ViewModels
{
    public class ContentFolderViewModel
    {
        public string Name { get; set; }
        public IEnumerable&amp;lt;DocumentViewModel&amp;gt; Documents { get; set; }
    }
}
&lt;/pre&gt;
&lt;pre&gt;namespace Alloy.Models.ViewModels
{
    public class DocumentViewModel
    {
        public string Name { get; set; }
        public string FileSize { get; set; }
        public string Url { get; set; }
        public string Description { get; set; }
        public bool EveryoneHasAccess { get; set; }
    }
}
&lt;/pre&gt;
&lt;p&gt;Extensions:&lt;/p&gt;
&lt;pre&gt;using EPiServer.Core;
using EPiServer.Security;
using EPiServer.ServiceLocation;
using EPiServer.Web.Routing;
using System;
using System.Security.Principal;

namespace Alloy.Business
{
    public static class MediaExtensions
    {
        private static UrlResolver urlResolver =&amp;gt; ServiceLocator.Current.GetInstance&amp;lt;UrlResolver&amp;gt;();
        private static PermissionService permissionService =&amp;gt; ServiceLocator.Current.GetInstance&amp;lt;PermissionService&amp;gt;();

        public static string GetSize(this EPiServer.Framework.Blobs.Blob blob)
        {
            using (var blobReader = blob.OpenRead())
            {
                var l = blobReader.Length;
                if(l &amp;lt; 1000)
                {
                    return $&quot;{l} bytes&quot;;
                }
                if(l &amp;lt; 1000000)
                {
                    return $&quot;{l / 1024} KB&quot;;
                }
                return $&quot;{l / 1056478} MB&quot;;
            }
        }

        public static Boolean IsAvailableToEveryone&amp;lt;T&amp;gt;(this T content) where T : IContent
        {
            return content.RoleHasAccess(new[] { &quot;Everyone&quot; }, AccessLevel.Read);
        }

        public static Boolean RoleHasAccess&amp;lt;T&amp;gt;(this T content, string[] roles, AccessLevel accessLevel) where T : IContent
        {
            var securedContent = content as ISecurable;
            var descriptor = securedContent.GetSecurityDescriptor();
            var identity = new GenericIdentity(&quot;doesn&#39;t matter&quot;);
            var principal = new GenericPrincipal(identity, roles);
            return descriptor.HasAccess(principal, accessLevel);
        }

        public static string GetUrl&amp;lt;T&amp;gt;(this T content) where T : IContent
        {
            return urlResolver.GetUrl(content.ContentLink);
        }
    }
}
&lt;/pre&gt;</id><updated>2016-10-11T15:44:11.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Quick &amp; Dirty Check for Role Permissions on IContent</title><link href="https://world.optimizely.com/blogs/egandalf/dates/2016/10/quick--dirty-check-for-role-permissions-on-icontent/" /><id>&lt;p&gt;For some reason, this was not an easy one to find, since most articles focus only on the current user and not using the Episerver APIs for lookups against other users or roles. Though I find it a bit strange, a customer needs to display a list of documents and indicate which among those documents are publicly accessible versus private (members-only).&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Thanks to David Knipe, my Twitter-based weathervane, I came up with a relatively quick and simple set of static methds for doing just that. Providing them here for posterity, allowing you to check a given role for a given permission via a quick extension whenever and wherever you need it.&lt;/p&gt;
&lt;pre&gt;public static Boolean IsAvailableToEveryone(this T content) where T : IContent
{
    return content.RoleHasAccess(new[] { &quot;Everyone&quot; }, AccessLevel.Read);
}

public static Boolean RoleHasAccess(this T content, string[] roles, AccessLevel accessLevel) where T : IContent
{
    var securedContent = content as ISecurable;
    var descriptor = securedContent.GetSecurityDescriptor();
    var identity = new GenericIdentity(&quot;doesn&#39;t matter&quot;);
    var principal = new GenericPrincipal(identity, roles);
    return descriptor.HasAccess(principal, accessLevel);
}
&lt;/pre&gt;</id><updated>2016-10-11T15:08:19.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Adding ACheck Plugin to Episerver TinyMCE</title><link href="https://world.optimizely.com/blogs/egandalf/dates/2016/8/adding-acheck-plugin-to-episerver-tinymce/" /><id>&lt;p&gt;&lt;span&gt;Every now and then, I get a minor challenge from the sales team. They need to demo something and they don&amp;rsquo;t have the availability to create or test it out. So they check if I&amp;rsquo;m willing to do it and, well, it gives me the chance to try something new in Episerver that I&amp;rsquo;ve not done before. Great! I&amp;rsquo;m in!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;On this occasion, I was tasked with the simple addition of the Accessibility Checker plugin for TinyMCE. Seriously, if you know what you&amp;rsquo;re doing, this should take only about a quarter of an hour, if that. Since I had never explored this side of things, it took me about two hours to figure it out (and over-come one of those blasted things that, like any developer, was bloody simple and I overlooked the shit out of it).&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;First off, the resources you&amp;rsquo;ll want to check out:&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;/link/f69063db609f45078d9de73be99b9a4c.aspx#addplugin&quot;&gt;&lt;span&gt;The official Episerver documentation for adding a TinyMCE plugin&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;http://www.atutor.ca/achecker/download.php&quot;&gt;&lt;span&gt;The download page for the specific TinyMCE plugin I&amp;rsquo;m adding&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;The &lt;a href=&quot;https://github.com/episerver/Commerce-Demo-Kit&quot;&gt;&lt;span&gt;Commerce Demo Kit&lt;/span&gt;&lt;/a&gt; project on Episerver&amp;rsquo;s Github, which &lt;a href=&quot;https://github.com/episerver/Commerce-Demo-Kit/blob/master/src/web/EditorDescriptors/TinyMcePlugins/AllowExtendedAttributes.cs&quot;&gt;&lt;span&gt;has a class you can use as an example&lt;/span&gt;&lt;/a&gt;. Even though this one is non-visual and the one I&amp;rsquo;m adding has a button, I found it useful.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;http://archive.tinymce.com/wiki.php/TinyMCE3x:Accessibility&quot;&gt;&lt;span&gt;The TinyMCE documentation where I found the AChecker plugin&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span&gt;First, add the folder structure for the plugin. According to the Episerver documentation (linked above), you have a couple of options.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;The first, and more secure option, is to use a Virtual Directory and map a path for ~Utils/Editor/tinymce/plugins to it (or just map ~/Util and add the rest of the folder structure to that root folder). The only real advantage I see here is that it helps keep the editor files out of the web root. But that&amp;rsquo;s not something I am worried about for a proof-of-concept.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;In my case, I wanted it to be something I could easily zip up and distribute to the sales team. So I opted for simply creating that same folder structure as part of my project.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Download the &amp;ldquo;achecker&amp;rdquo; plugin (via the link above), uncompress it, and add it to the plugins folder you just created.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Next, I created the class, which is just an empty class with an attribute used to inform Episerver of the additional plugin and its configuration options. Definitely check out the documentation above for the complete set of options. Mine uses the TinyMCEPluginButton attribute because it&amp;rsquo;s a visual plugin (it has a button).&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;using EPiServer.Editor.TinyMCE;

namespace OxxCommerceStarterKit.Web.EditorDescriptors.TinyMcePlugins
{
    [TinyMCEPluginButton(
        PlugInName = &quot;acheck&quot;,
        ButtonName = &quot;acheck&quot;,
        GroupName = &quot;misc&quot;,
        LanguagePath = &quot;admin/tinymce/plugins/acheck&quot;,
        IconUrl = &quot;Editor/tinymce/plugins/acheck/img/acheck.gif&quot;,
        Description = &quot;Accessibility Checker&quot;,
        DisplayName = &quot;Accessibility Check&quot;)]
    public class ACheck
    {
    }
}
&lt;/pre&gt;
&lt;p&gt;&lt;span&gt;The properties are as follows:&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;PlugInName &amp;amp; ButtonName - These both need to be the same as the name of the plugin being added. If you inspect the *_src.js file of the plugin, you&amp;rsquo;ll find the plugin being added with a name. There&amp;rsquo;s also code in the file for adding a named button. I assume these can be different, but it&amp;rsquo;s likely convention for them to be the same, if this plugin is any example.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;GroupName - Puts the button into one of the groups in the admin view of Episerver. These really are standard TinyMCE button groups. It doesn&amp;rsquo;t affect where the button is placed in the toolbar (you get to configure that), but rather where you find the button in order to add it to your custom tools.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;LanguagePath - If you know Episerver, you know this one. I only configured the English language.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;I assume Description and DisplayName to be self-explanatory.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span&gt;Really, the approach taken should work for any plugin. However, there might be compatibility issues or conflicts with the plugins Episerver has created. Still, it&amp;rsquo;s pretty cool to know that it&amp;rsquo;s a simple matter of a plugin, a class, and (optionally) a language file to add some new functionality to Episerver.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Relevant source files and basic instructions &lt;a href=&quot;https://github.com/egandalf/Accessibility-Checker-for-Episerver-TinyMCE&quot;&gt;&lt;span&gt;can be found on Github&lt;/span&gt;&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Happy coding.&lt;/span&gt;&lt;/p&gt;</id><updated>2016-08-05T19:20:30.3200000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>An Ektron-to-Episerver Starter Kit</title><link href="https://world.optimizely.com/blogs/egandalf/dates/2015/11/an-ektron-to-episerver-starter-kit/" /><id>&lt;p&gt;Over the last several months, I’ve been working with Allan Thraen and others to find and research ways to make the transition from Ektron CMS to Episerver Digital Experience Cloud as smooth as possible.&lt;/p&gt;
&lt;p&gt;As was discussed last week at Ascend in Las Vegas, there is no push-button solution. Too much between the systems lacks a one-to-one mapping and Ektron’s platform allowed too much leeway in avoiding best practices for any fully-automated utility to be practical to develop and maintain.&lt;/p&gt;
&lt;p&gt;Instead, I focused my energies on a handful of helpful paths.&lt;/p&gt;
&lt;p&gt;For this post, I’d like to introduce you to a bit of code worked up between Allan and myself. If you want to skip the explanation, links to both sets of code are at the bottom of the article.&lt;/p&gt;
&lt;p&gt;The first bit of code is Allan’s addition of a REST API to Episerver. This code extends the ServiceAPI, which is used to provide authentication and authorization protection, to add some content endpoints to your Digital Experience Cloud instance. For complete details, I suggest you &lt;a href=&quot;https://github.com/AThraen/ServiceAPIExtensions&quot;&gt;follow through to his Github repo and readme&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://github.com/egandalf/ContentTransferStarterKit&quot;&gt;second repository&lt;/a&gt;, I’ve coded up a few class libraries and console applications. Full details, including limitations, are in the ReadMe, but the general idea is to first move an Ektron Menu structure into Epi to be used as the basis for the content tree. Then, selectively move Ektron content from a Folder in the source CMS into a selected location within the newly generated content tree in the CMS.&lt;/p&gt;
&lt;p&gt;Note that these are not officially supported projects, though Allan and I want to provide as much community support as we can. Feel free to file issues through Github and reach out to us if you would like to help contribute or create your own forks of either project (though we may want to incorporate anything cool you build :).&lt;/p&gt;
&lt;p&gt;Github Repos:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/AThraen/ServiceAPIExtensions&quot;&gt;&lt;span&gt;Allan Thraen’s Service API Extensions&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;&lt;a href=&quot;https://github.com/egandalf/ContentTransferStarterKit&quot;&gt;James Stout’s Ektron Integration &amp;amp; Console Apps&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;</id><updated>2015-11-18T22:46:01.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Tune In Tomorrow for a New #EPiDev Fanmail Episode!</title><link href="https://world.optimizely.com/blogs/egandalf/dates/2015/6/tune-in-tomorrow-for-a-new-epidev-fanmail-episode/" /><id>&lt;p&gt;The best part of hosting EPiDev shows is the questions we get from viewers - either live via Twitter during the event, using the #EPiDev hashtag, or in between shows via email. For this show, I reached out to several Ektron customers who are either getting started in EPiServer, or very interested. At the very least, each of the contributors has had EPiServer running locally to dabble a bit.&lt;/p&gt;
&lt;p&gt;When you&#39;re just getting started, questions are sure to come up. Sometimes these questions are comparative between EPiServer and your legacy platform. At other times, it&#39;s just a matter of clearing the weeds a bit and not getting too lost or frustrated. Either way, we want to provide as many answers as we can.&lt;/p&gt;
&lt;p&gt;So for this episode, we have nearly 20 questions from our audience and we will do our best to provide quick-fire answers. Some questions, I&#39;m sure, will lead into full-fledged episodes of their own.&lt;/p&gt;
&lt;p&gt;Joining me are EPiServer&#39;s Allan Thraen and the ever-amazing Kristoffer&amp;nbsp;Sj&amp;ouml;berg.&lt;/p&gt;
&lt;p&gt;If you have questions or want us to cover a topic, please email me at: james.stout@episerver.com&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://plus.google.com/events/ciqcr5t2153amiantkbd1t3m9n4&quot;&gt;Tune in tomorrow at 10 AM EDT (14:00 GMT) to watch this episode that&#39;s entirely focused on you, our viewers&lt;/a&gt;.&lt;/p&gt;</id><updated>2015-06-02T17:33:30.6670000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Building a Carousel using Global Razor Templates in EPiServer MVC</title><link href="https://world.optimizely.com/blogs/egandalf/dates/2015/4/building-a-carousel-using-global-razor-templates-in-episerver-mvc/" /><id>&lt;p&gt;I&amp;rsquo;m currently working on my first EPiServer project - one that I hope will serve as a well-commented reference and starter app. Going through this process somewhat slowly and retyping all of the code that I&amp;rsquo;m borrowing from other places is really helping me with some deep-learning of the platform.&lt;/p&gt;
&lt;p&gt;In this process, I&amp;rsquo;m really starting to appreciate the extensibility of the platform and the combined power of EPiServer and MVC. While I still feel like I have more questions than answers when it comes to specifics of the technology, I also am getting a firm grasp of EPiServer development and am appreciating how it is simultaneously awesome for developers and for business users alike.&lt;/p&gt;
&lt;p&gt;An Ektron and EPiServer comparison blog may be forthcoming, but for the moment I wanted to share a really cool little trick that I read about on Pride Parrot, &lt;a href=&quot;http://prideparrot.com/blog/archive/2012/9/simplifying_html_generation_using_razor_templates&quot;&gt;Simplifying html generation in code using Razor templates&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The HTML template I&amp;rsquo;m using&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Carousel slides would be created by dragging and dropping ArticlePage items into the ContentArea. Displaying those items using a carousel rendering was obvious for an individual item, but in order to display them as a carousel, I needed to treat them as a list of similar objects and the traditional PagePartial rendering didn&amp;rsquo;t seem to fit the bill.&lt;/p&gt;
&lt;p&gt;A traditional @helper template approach would do the job, provided I could correctly parse the information inside the ContentArea and pass it over, but I really didn&amp;rsquo;t like the idea of having what is essentially a PagePartial rendering residing as a template within my StartPage Index rendering.&lt;/p&gt;
&lt;p&gt;The article referenced above provides a simple way to take the template I created for my list of articles and move it into a place where it can be used and called globally from my other views. If you&amp;rsquo;re interested in also calling the template from code, see the above article, though I have not (yet) taken it that far in my own project.&lt;/p&gt;
&lt;p&gt;The process is exceedingly simple.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;First, if you don&amp;rsquo;t already have an App_Code folder in your project, then add one by going to Visual Studio, open the Solution panel and right-click on the EPiServer project. Then choose Add &amp;gt; Add ASP.NET Folder &amp;gt; App_Code.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Within the App_Code folder, I created a new view named Templates.cshtml. I pasted my helper into this view, like so:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;@helper CarouselTemplate(List&amp;lt;BlogSample.Helpers.CarouselItem&amp;gt; carouselItems)
{
    &amp;lt;div class=&quot;col-md-12&quot;&amp;gt;
        &amp;lt;div class=&quot;content-article&quot;&amp;gt;
            &amp;lt;div id=&quot;home-slider&quot; class=&quot;carousel slide&quot; data-ride=&quot;carousel&quot;&amp;gt;
                &amp;lt;ol class=&quot;carousel-indicators&quot;&amp;gt;
                    @for (int i = 0; i &amp;lt; carouselItems.Count(); i++)
                    {
                        &amp;lt;li data-target=&quot;#home-slider&quot; data-slide-to=&quot;@i&quot; class=&quot;@(i == 0 ? &quot;active&quot; : string.Empty)&quot;&amp;gt;&amp;lt;/li&amp;gt;
                    }
                &amp;lt;/ol&amp;gt;
                &amp;lt;div class=&quot;carousel-inner&quot;&amp;gt;
                    @for (int i = 0; i &amp;lt; carouselItems.Count(); i++)
                    {
                        &amp;lt;div class=&quot;item @(i == 0 ? &quot;active&quot; : string.Empty)&quot;&amp;gt;
                            &amp;lt;img src=&quot;@carouselItems[i].ImageUrl&quot; alt=&quot;@carouselItems[i].TitleText&quot; /&amp;gt;
                            &amp;lt;div class=&quot;carousel-caption&quot;&amp;gt;
                                &amp;lt;h3&amp;gt;&amp;lt;small&amp;gt;&amp;lt;span class=&quot;caption-meta-date&quot;&amp;gt;@carouselItems[i].PublicationDate.ToString(&quot;MM/dd&quot;)&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;@carouselItems[i].PublicationDate.Year&amp;lt;/span&amp;gt;&amp;lt;/small&amp;gt; @carouselItems[i].TitleText&amp;lt;/h3&amp;gt;
                                &amp;lt;p&amp;gt;@carouselItems[i].Tagline&amp;lt;/p&amp;gt;
                            &amp;lt;/div&amp;gt;
                        &amp;lt;/div&amp;gt;
                    }
                &amp;lt;/div&amp;gt;
                &amp;lt;a class=&quot;left carousel-control&quot; href=&quot;#home-slider&quot; role=&quot;button&quot; data-slide=&quot;prev&quot;&amp;gt;
                    &amp;lt;span class=&quot;glyphicon glyphicon-chevron-left&quot;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;/a&amp;gt;
                &amp;lt;a class=&quot;right carousel-control&quot; href=&quot;#home-slider&quot; role=&quot;button&quot; data-slide=&quot;next&quot;&amp;gt;
                    &amp;lt;span class=&quot;glyphicon glyphicon-chevron-right&quot;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;/a&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, calling the global template just works. The View name becomes a namespace of sorts, so the format for calling the template in my case is Templates.CarouselTemplate, and I use it just as I would any other razor template, except that it&amp;rsquo;s globally accessible.&lt;/p&gt;
&lt;p&gt;In my case, however, simply using it as a template wasn&amp;rsquo;t enough to get the results I want. Primarily, my carousel will be made up of ArticlePage items. I have no intention of doing otherwise, but that doesn&amp;rsquo;t mean I don&amp;rsquo;t want that flexibility in the future. So what I&amp;rsquo;ll do instead is create my own Html Helper that will take the items within the ContentArea - whatever they are - and process them to be used as carousel slides. Here&amp;rsquo;s my logic for providing a drag and drop area versus the carousel helper:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;@if (EPiServer.Editor.PageEditing.PageIsInEditMode)
{
    @Html.PropertyFor(m =&amp;gt; m.CurrentPage.FeaturedContentArea, new { CssClass = &quot;row&quot;, tag = BlogSample.Global.ContentAreaTags.Carousel })
}
else
{
    @Html.Carousel(Model.CurrentPage.FeaturedContentArea != null ? Model.CurrentPage.FeaturedContentArea.Items : new List&amp;lt;ContentAreaItem&amp;gt;(), Templates.CarouselTemplate)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I do this because I want the author to retain the drag and drop UI plus be able to view the slides individually for reordering. The else condition will provide a non-editable view for the site visitor to see a fully-functional carousel.&lt;/p&gt;
&lt;p&gt;In order to build the HTML helper, I first need all of my &amp;ldquo;carousel enabled&amp;rdquo; items to have the same properties. So I created an inheritable class that would contain those content properties and present them in a Carousel tab via the Properties view.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using System;
using System.ComponentModel.DataAnnotations;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.DataAnnotations;
using EPiServer.SpecializedProperties;

namespace BlogSample.Models.Pages
{
    [ContentType(DisplayName = &quot;CarouselPageData&quot;, GUID = &quot;684c86d7-6a66-44f3-82d5-9b3a6277ab90&quot;, Description = &quot;&quot;)]
    [AvailableContentTypes( 
        Availability = Availability.None,
        ExcludeOn = new[] { typeof(StartPage), typeof(BlogPage), typeof(ArticlePage) })]
    public class CarouselPageData : PageBase
    {
        [Display(Name = &quot;Short Title&quot;,
            Description = &quot;Used in places where the actual title might be too long. For example, the home page carousel.&quot;,
            GroupName = Global.GroupNames.Carousel,
            Order = 100)]
        public virtual string ShortTitle { get; set; }

        [Display(Name = &quot;Featured Image&quot;,
            Description = &quot;Will appear in places like the Homepage Carousel.&quot;,
            GroupName = Global.GroupNames.Carousel,
            Order = 200)]
        public virtual ContentReference FeaturedImage { get; set; }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All I need do to make sure my Article page is available for this Carousel is to have it inherit from my CarouselPageData class and make sure that it has a PagePartial rendering configuration with the same tag as my ContentArea Property.&lt;/p&gt;
&lt;p&gt;Inside ArticlePage.cs:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class ArticlePage : CarouselPageData&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside TemplateCoordinator.cs:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;viewTemplateModelRegistrator.Add(typeof(ArticlePage), new TemplateModel

{

&amp;nbsp; &amp;nbsp; Name = &quot;ArticleAsCarouselSlide&quot;,

&amp;nbsp; &amp;nbsp; DisplayName = &quot;Article as Carousel Slide&quot;,

&amp;nbsp; &amp;nbsp; Default = false,

&amp;nbsp; &amp;nbsp; Tags = new[] { Global.ContentAreaTags.Carousel },

&amp;nbsp; &amp;nbsp; AvailableWithoutTag = false,

&amp;nbsp; &amp;nbsp; Path = PartialPath(&quot;ArticlePageCarouselSlide&quot;)

});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I now have the right properties, a rendering for when I drag and drop, and the global template I need for rendering the visitor view. What&amp;rsquo;s lacking is the Html.Carousel helper itself, which is used in the view above as well as a common ViewModel for the display within that template.&lt;/p&gt;
&lt;p&gt;The ViewModel is rather simple, in the way of most ViewModels. The only extra logic in this one is a bit to get the publication date as I want the author to be able to set a publication date on this content manually (helpful for imported content - unless there&amp;rsquo;s another solution there I&amp;rsquo;ve not yet discovered). If a manual date has been set, then I want to use it, otherwise fallback to the content item&amp;rsquo;s initial publish date. Obviously, this is still a work in progress and there will be no hard-coded text in the final product.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using BlogSample.Business.Extensions;
using BlogSample.Models.Pages;
using EPiServer.Core;
using EPiServer.Web.Routing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BlogSample.Helpers
{
    public class CarouselItem
    {
        public PageData Page { get; set; }
        public string PageUrl { get; set; }
        public string TitleText { get; set; }
        public string ImageUrl { get; set; }
        public string Tagline { get; set; }
        public DateTime PublicationDate { get; set; }

        public CarouselItem(CarouselPageData carouselContent)
        {
            this.Page = carouselContent;
            this.PageUrl = UrlResolver.Current.GetUrl(carouselContent.ContentLink);
            this.TitleText = carouselContent.ShortTitle;
            this.ImageUrl = carouselContent.FeaturedImage.ImageUrl();
            this.Tagline = &quot;Can&#39;t stop me now!&quot;;
            this.PublicationDate = GetPublicationDate(carouselContent);
        }

        private DateTime GetPublicationDate(CarouselPageData carouselContent)
        {
            if (carouselContent is ArticlePage)
            {
                var articleContent = carouselContent as ArticlePage;
                if (DateTime.Compare(articleContent.PublicationDate, DateTime.MinValue) != 0)
                {
                    return articleContent.PublicationDate;
                }
            }

            return carouselContent.StartPublish;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I borrowed what I could from several places to create my Carousel helper, but mostly from the Alloy HtmlHelpers class used for menu items.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public static IHtmlString Carousel(
    this HtmlHelper helper,
    IList&amp;lt;ContentAreaItem&amp;gt; itemList,
    Func&amp;lt;List&amp;lt;CarouselItem&amp;gt;, HelperResult&amp;gt; itemTemplate = null)
{
    itemTemplate = itemTemplate ?? GetDefaultCarouselItemTemplate(helper);
    var contentLoader = ServiceLocator.Current.GetInstance&amp;lt;IContentLoader&amp;gt;();
    var contentAreaContentLinks = itemList.Select(c =&amp;gt; c.ContentLink).ToList();

    var contentList = new List&amp;lt;CarouselItem&amp;gt;();
    CarouselPageData cdata;
    foreach (var item in itemList)
    {
        cdata = null;
        contentLoader.TryGet&amp;lt;CarouselPageData&amp;gt;(item.ContentLink, out cdata);
        if (cdata != null)
        {
            contentList.Add(new CarouselItem(cdata));
        }
    }

    var buffer = new StringBuilder();
    var writer = new StringWriter(buffer);
    itemTemplate(contentList).WriteTo(writer);

    return new MvcHtmlString(buffer.ToString());
}

private static Func&amp;lt;IList&amp;lt;CarouselItem&amp;gt;, HelperResult&amp;gt; GetDefaultCarouselItemTemplate(HtmlHelper helper)
{
    return x =&amp;gt; new HelperResult(writer =&amp;gt; writer.Write(helper.PageLink(x.First().Page)));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All together, I get an editor view with drag and drop placement, but I also get a fully functional Carousel rendering that is available globally so I can call it from multiple templates, should I need to do so. In addition, all any content item needs is to inherit my CarouselPageData class in order to be used as a slide.&lt;/p&gt;
&lt;p&gt;This is still very early in development of this project and any or all of the code above is subject to change as the project proceeds and I continue to learn about development in EPiServer and MVC. The entire project is being &lt;a href=&quot;https://github.com/egandalf/EPiServer-Blog-Sample-Site&quot;&gt;published to GitHub as well&lt;/a&gt;, so you can see what I&amp;rsquo;m up to and check in periodically on my progress.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;</id><updated>2015-04-06T18:09:07.1800000Z</updated><summary type="html">Blog post</summary></entry></feed>