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.
Wednesday, March 20, 2013
Set up an EPiServer site in a snap
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!
Consumers prefer the tablet when spending time shopping. During the past holiday shopping season 13,5 % of all online sales were transacted via tablets.
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:
- http://forums.iis.net/t/1176740.aspx
- http://www.iis.net/downloads/microsoft/application-initialization
- https://www.simple-talk.com/blogs/2013/03/05/speeding-up-your-application-with-the-iis-auto-start-feature/
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
Wednesday, March 6, 2013
Nansen - The Great e-Commerce Infographics Saga, Episode 7 - T-commerce
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.