Wednesday, March 20, 2013

Set up an EPiServer site in a snap

Do you also work at a digital agency where you have several dozen of customer sites? In that case I bet you have spent some time setting up sites in IIS web server, followed by trying to remember the path to the HOSTS file and finding an appropriate EPiServer license file that you then copy to the site.

Sure, it does not take that long to do it all but it sure as heck is boring and repetitive. It can easily be avoided by some automation.

WIA is the new kid on the block

To automate this process I decided to start a project called WIA, that is an easy to use command line tool. It will do all the steps required to set up an EPiServer CMS site, all before you have had a chance to take another sip of your coffee.



Actually, WIA will do all the following steps for you:

- Figure out what kind of project you have (EPiServer and .NET versions etc).
- Create a new site in IIS with appropriate configuration.
- Add a site mapping in EPiServerFramework.config matching your new IIS site.
- Update the HOSTS file with an entry for the web project's URL.
- Copy a license file for EPiServer CMS to the web directory.
- Build the solution and display any errors that occurred.
- Ping the site so that it starts up.

Sound good. Now let me try it

Does this tool sound like something that would improve your workflow? Head over to the git-repo at https://github.com/nansen/wia for instructions how to install the tool and its usage.

If you find problems please feel free to submit issues at the Github page, or even a pull-request if you are feeling kind.

Friday, March 15, 2013

Hide tabs in EPiServers edit mode if not in certain role



Recently I had a customer requirement where a certain role should only be able to edit one specific property on the page.
I couldn't find any built in support in EPiServer for this, so I had to figure out another way to solve this problem.

Inspired by this blog post by David Knipe I decided to go for an Edit plugin with the following code:


 [GuiPlugIn(Area = PlugInArea.EditPanel)]
 class HideTabsFromRole : ICustomPlugInLoader
 {
  public PlugInDescriptor[] List()
  {
   EditPanel.LoadedPage += EditPanel_LoadedPage;

   return new PlugInDescriptor[0] { };
  }

  void EditPanel_LoadedPage(EditPanel sender, LoadedPageEventArgs e)
  {
   if (!HttpContext.Current.User.IsInRole("MyRole")) return;

   foreach (var propertyData in e.Page.Property.Where(propertyData => propertyData.Name != "MyProperty"))
   {
    e.Page.Property[propertyData.Name].DisplayEditUI = false;
   }
  }
 }


Basically what I'm doing is first checking if the user is in the role and if so then hide the properties, except for the property I want to display for this role. If there's no visible properties left on the tab the actual tab will not be rendered. The last part is default functionality in episerver.

Maybe this solution by Mark Everard would be a neater way of solving this problem, but this time I chose to take a shortcut.


Thursday, March 14, 2013

T-commerce larger than m-commerce for the first time!


Since January 2013 are different tablets (iPad, Android) responsible for more traffic than smartphones. All according to a new Adobe document. More than 100 bil­lion vis­its to 1000+ web­sites world-wide was evaluated. 

Consumers prefer the tablet when spending time shopping. Dur­ing the past hol­i­day shop­ping sea­son 13,5 % of all online sales were trans­acted via tablets. 



13926_di_tablet_smartphone_growth

13926_di_global_traffic_device

Tuesday, March 12, 2013

Quickly create (simple) serialization classes from xml

So Visual Studio 2012 and .net 4.5 has a pretty nifty feature for when you quickly need to handle some arbitrary xml document, "paste xml as classes". In order to do this, copy your xml to the clipboard, place the cursor in a code file, utside any class definition, and go to the edit menu -> paste special -> paste xml as classes. Voila! You now have something you can use to serialize and deserialize your xml. Very simple and ugly, but sure beats hacking them yourself.

This feature require a .net 4.5 project in VS2012, but only the actual function, the generated classes can be used in .net 4 and older.

Read more here: http://msdn.microsoft.com/en-us/library/hh371548.aspx

Sunday, March 10, 2013

IIS Application Warmup and Initialization

Here are a few handy tips for getting sites warmed up after restart.

Application Initialization is the new replacement for the former “Application Warm-up” beta module that has been around for a few years. Application Initialization extends the features in the earlier Application Warm-up beta and gives significant new functionality. Application Initialization is also built-in in IIS 8.0, and the module delivers the same functionality for IIS 7.5.

Summary and downloads:



Friday, March 8, 2013

Nansen - The Great e-Commerce Infographics Saga, Episode 9 - A E-com Checklist!

A real infographics maze. Don't miss this The 91 Point Ecommerce Optimization Checklist from CueBlocks.


Thursday, March 7, 2013

Nansen - The Great e-Commerce Infographics Saga, Episode 8 - Global Commerce

Make sure to take a look at the value of the global market in this SearchLaboratory Global Commerce overview:

Wednesday, March 6, 2013

Nansen - The Great e-Commerce Infographics Saga, Episode 7 - T-commerce

This graphic by Catalogspree about t-commerce and Why tablets are the future of e-commerce is awesome!

Tuesday, March 5, 2013

Creating a XForm Block in EPiServer 7 MVC (With Working Validation)

With the flexibility that block types offer in EPiServer 7, it would make sense that one common feature that people would want is an XForm in a block, so editors can freely add, move, and reuse blocks with forms around their website. In a recent project, this was one of our requirements.

Although a solution to getting XForms to work inside a block type has been solved, it doesn't fully support validation, which is a common requirement. Also, the documentation is still lacking when it comes to explaining how to integrate XForms in EPiServer 7 MVC, both within a page type and a block type.

The Solution


The solution to this revolved heavily around making sure the action URL on the form was correct (so we stay on the page the the block is located) and sharing the controller's ViewData with all controllers that needed it (both page controllers and block controllers).

/Models/Blocks/XFormBlock.cs


This is just a simple block type with an XForm property.

[ContentType(GUID = "49754310-E0ED-4C95-AA69-C155323E0AA9")]
public class XFormBlock : BlockData
{
    [Display(GroupName = SystemTabNames.Content)]
    public virtual XForm Form { get; set; }
}

/Models/ViewModels/XFormViewModel.cs


This is just a simple view model that transfers the XForm property and the ActionUrl string to the block's view.

public class XFormViewModel
{
    public XForm Form { get; set; }
    public string ActionUrl { get; set; }
}

/Controllers/BasePageController.cs


This is the most important piece of the puzzle, since it handles all the XForm actions, as well as sets the ViewData that's used in the block controller.

The first main method that we override is the OnResultExecuting() method, which sets the ViewData in the TempData collection after a page controller sets it. Without this, the block controller will have a different ViewData, which makes all the validation information go away.

The second main method we override is OnActionExecuting(), which handles the transfer of the ViewData between the XForm methods Success() and Failed() to the page controllers they are redirecting to through the RedirectToAction("Index") call.

public class BasePageController<T> : PageController<T> where T : PageData
{
    private readonly XFormPageUnknownActionHandler _xformHandler;

    public BasePageController()
    {
        _xformHandler = new XFormPageUnknownActionHandler();
    }

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (TempData["ViewData"] != null)
        {
            ViewData = (ViewDataDictionary)TempData["ViewData"];
        }

        base.OnActionExecuting(filterContext);
    }

    protected override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        TempData["ViewData"] = ViewData;

        base.OnResultExecuting(filterContext);
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public virtual ActionResult Success(XFormPostedData xFormPostedData)
    {
        return RedirectToAction("Index");
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public virtual ActionResult Failed(XFormPostedData xFormPostedData)
    {
        return RedirectToAction("Index");
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public virtual ActionResult XFormPost(XFormPostedData xFormpostedData)
    {
        return _xformHandler.HandleAction(this);
    }
}

/Controllers/XFormBlockController.cs


In the block's controller, we grab the ViewData that we saved in the BaseController. Then, we instantiate our view model that holds the XForm property and the ActionUrl string for the view.

The important part of this is how we build out the ActionUrl. We can get the currentPage data from the PageRouteHelper, then get the virtual path to the page from the currentPage using a UrlResolver, so we always POST to the page that the block is on. The rest of the values in the query string are to set the actions that the XForm handler uses if the submission was successful or unsuccessful.

public class XFormBlockController : BlockController<XFormBlock>
{
    public override ActionResult Index(XFormBlock currentBlock)
    {
        if (TempData["ViewData"] != null)
        {
            ViewData = (ViewDataDictionary)TempData["ViewData"];
        }

        var viewModel = new XFormViewModel();

        var pageRouteHelper = ServiceLocator.Current.GetInstance<PageRouteHelper>();
        PageData currentPage = pageRouteHelper.Page;

        if (currentBlock.Form != null && currentPage != null)
        {
            viewModel.Form = currentBlock.Form;

            var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
            var pageUrl = urlResolver.GetVirtualPath(currentPage.ContentLink);

            var actionUrl = string.Format("{0}XFormPost/", pageUrl);
            actionUrl = UriSupport.AddQueryString(actionUrl, "XFormId", viewModel.Form.Id.ToString());
            actionUrl = UriSupport.AddQueryString(actionUrl, "failedAction", "Failed");
            actionUrl = UriSupport.AddQueryString(actionUrl, "successAction", "Success");

            viewModel.ActionUrl = actionUrl;
        }

        return PartialView(viewModel);
    }
}

/Views/XFormBlock/Index.cshtml


This is just a simple view for the block. The primary thing that is different compared to how we normally output page properties is that we need to set the action on the form.

@model XFormViewModel

@Html.ValidationSummary()

@using (Html.BeginXForm(Model.Form, new { Action = Model.ActionUrl }))
{
    Html.RenderXForm(Model.Form);
} 

And that's it. With this solution, you don't need to worry handling the XForm in any specific page controllers that the block lives in, although you could easily just override the XFormPost() method in the BasePageController if needed. This also supports the built-in 'Save to database' and/or 'Send e-mail' submit options.

Nansen - The Great e-Commerce Infographics Saga, Episode 6 - Effective product pages

Here's another great e-Commerce infographic, today about How to Create Effective E-commerce Product Pages from very good Elastic Path Getelastic.com blog.


Monday, March 4, 2013

Nansen - The Great e-Commerce Infographics Saga, Episode 5 - Invest in CXM

Oh, don't miss this great e-Commerce infographic about Why Companies Should Invest in the Customer Experience by Zendesk.


Friday, March 1, 2013

Nansen - The Great e-Commerce Infographics Saga, Episode 4 Ecommerce performance

Here's a great e-Commerce infographic about Cost of poor performance by Smartbear.