Experience Sitecore ! | All posts by martin

Experience Sitecore !

More than 200 articles about the best DXP by Martin Miles

Sitecore Boilerplate - the repository of best practices all at the same place

I decided to create an ultimate "boilerplate" solution for Sitecore, implementing all the best Sitecore practices in one place, well documented and cross-linked with the support on this blog.

As a multi-language website with Experience Editor (ex. Page Editor) support utilizing with Glass Mapper, Lucene indexes and test-driven codebase and much more working well all together - it will be a perfect place for newbies to familiarize themselves with Sitecore platform. It aims also to simplify work of more senior Sitecore developers in terms of quickly searching for desired features and grabbing them into their working solutions.

The project originated out of my R&D activities as I decided it would be beneficial to share my workouts with Sitecore community. Any suggestions, comments and criticism are highly welcome!

List of the features I desire to supply into Sitecore boilerplate:

  • Support for Page Editor
  • Usage of Glass Mapper for ORM purposes
  • Unit testable code
  • Synchronization of user-editable content from CD environment to CM and further re-publish to the rest of CDs
  • Support for multi-language environment
  • Custom Lucene indexes
  • Custom personalisation of components and data
  • Workflows based on user permissions
  • Make all mentioned above working together as a solid and stable website
  • Implement new Sitecore 8 marketing features on top of that

.. for the moment I have planned and implemented several of mentioned features as a starting point, so it is coming soon on GitHub and further blog posts here.

Sitecore MVC areas as pluggable separate DLL - making areas further more independent!

I want to share my experience of implementing MVC Areas as an individually pluggable (into the host website) DLL, that contains the code of specific area: controllers, models / view models, some area-specific DLL. The proof of concept was created by my great colleague Chandra Prakash, I decided to pick it after him and implemented in our product, so now it is 8 month as it works in production without any issues at all.


We are working in a big enterprise Sitecore-hosted project with more than hundred of developers, so each deployment process may bring a pain. Pluggable areas implementation has proven its concept and helped us to keep updating only those parts of entire multisite Sitecore solution that have been updated, without any risk of affecting the rest!


Features:

  • no need to code anything in a host website, thus:
  • no need to rebuild whole big outer solution - just rebuild (and replace) DLL
  • no more need to struggle with complex dependencies
  • simplified update: drop DLL into host website bin folder, and copy some static files referenced by this DLL - js, css, cshtml, img.

In day-to-day usage you will have the only one minor overhead of that implementation: 2 extra fields in controller rendering. In fact, instead of standard Controller Rendering we are using Area Controller Rendering that is derived from Controller Rendering just with addition of 2 extra fields required to resolve the area on a fly:



Sound attractive, isn't it? Then look how it is implemented - I address to the original article with more detailed explanations.




Editing content on a CD server. Part 1. MVC ajax request to controller

Imagine the situation, when you need to have a page with an updatable text, for instance:

This div becomes editable as you click it

Now the next logical step would be to fire on blur client event (it happens when out focus out of div, ending the editing mode) and send changed content somewhere to the back end. Something simple like jQuery snippet below can handle that:


$('#editField').blur(function () {
        
    $.ajax({
        url: 'some/backend/url/to/post',
        data: { name: value },
        type: 'post',
        success: function () {
            // handle success 
        },
        error: function () {
            // handle error
        }
    });
});

So far, so good. The very next question would be - how do I create an endpoint in Sitecore to support that ajax post request and how do I pass the data and handle positive and negative outcomes? I assume, the back end should have some MVC controller action, that does some back end job of storing my data and returning JSON object back to client script.

So in order to make this work we register MVC routes, this is referenced from Application_Start event handler and is usually implemented in App_Start folder.
    public class Application : Sitecore.Web.Application
    {
        protected void Application_Start()
        {
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
    }
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.MapRoute(
                 name: "ajax",
                 url: "api/Ajax/{action}/{id}",
                 defaults: new { controller = "Ajax", 
                                 action = "DefaultActionMethodName", id = UrlParameter.Optional }
               );
        }
    }

This route binds all the /api/Ajax requests to be served by AjaxController class. But there is one more setting you require to do in order for your request to go the right direction - in config file set up a custom handler that will intercept that types of requests:

  
    ...
    
    ...

Controller action method being called is specified by caller, and will call DefaultActionMethodName as a default fallback if missing, passing id is optional. Here is the controller:

    public class AjaxController : Controller
    {
        [HttpPost]
        public ActionResult PostComment(string id, PostCommentViewModel model)
        {
            // implement backend logic here

            return Json();
        }
    }

 Controller accepts id as a parameter automatically resolved from URL, as specified in route we configured earlier. It also accepts and binds JSON object that we send as data into PostCommentViewModel object that automatically comes into controller as second parameter. Here is its implementation:

    public class PostCommentViewModel
    {
        [AllowHtml]
        public string Comment { get; set; }
    }
So, we can now finalize jQuery snippet that handles blur effect and sends data to controller. I have intentionally simplified it using external JavaScript objects, for clarity of understanding, you would normally avoid using global JavaScript variables in production code. These objects are used to keep state between ajax calls and to call server only when content is modified indeed. 
If there was an error on server, script retains previous value. If request worked out successfully with a status code 200 (OK) the we store updated value into <div> tag.  
var contents = $('#editField').html();
var id = '@Html.Sitecore().CurrentItem.ID';

var data = {};

$('#editField').blur(function () {
    if (contents != $(this).html()) {

        $.ajax({
            url: '/api/Ajax/PostComment/' + id,
            data: { Comment: $('#editField').html().trim() },
            type: 'post',
            success: function () {
                contents = $('#editField').html();
                var k = 0;
            },
            error: function () {
                $(this).html(contents);
            }
        });

        contents = $(this).html();
    }
});

On the server side there is not much to do with it - just save to database and return the result. Here is the final code of PostComment action of AjaxController:

 public class AjaxController : BaseController
    {
        [HttpPost]
        public ActionResult PostComment(string id, PostCommentViewModel model) // change to HtmlString
        {
            //Response.StatusCode = 500; 

            Database database = Sitecore.Context.Database;
            var item = database.GetItem(id);

            using (new Sitecore.SecurityModel.SecurityDisabler())
            {
                item.Editing.BeginEdit();
                try
                {
                    item.Fields["Comment"].Value = model.Comment;
                }
                finally
                {
                    item.Editing.EndEdit();
                }
            }

            item = database.GetItem(id);
            var val = item.Fields["Comment"].Value;

            return Json(id + " |" + model.Comment);
        }
    }

Editing content on a CD server. Part 2. Event Queue to sync data with master

... this blog is a next step after Editing content on a CD server. Part 1. MVC ajax request to controller.

Now we got a question on how to implement server logic to store the value. It may seem pretty straightforward for a moment - get item by its ID, update the field and return result. But in fact it's not, due to Sitecore's architectural principles.


Our page is running on a CD (content delivery server) and the item we're trying to update comes from CD database (traditionally called "web"), so if we edit and update item on "web" database - we'll get into situation when "web" contains updated version, while "master" doesn't. Publishing is one-way process of copying items from CM to CD databases (or simply from "master" to "web") so with next publishing we may overwrite updated item in web with outdated previous version from master. Writing directly to CM database is a violation of architectural principles, while it is technically possible on your developer "default" Sitecore installation, in real world Sitecore CD servers do not keep "master" connection string, making this process impossible. So, what should we do in that case?


Luckily, there are several ways of solving this scenario, each has its own pros and cons, so it is worth of thinking well ahead which (and if) is applicable to your solution.

Solution 1: Allocate a separate database in parallel with web, to store all user editable content. Normal items and non-user content will remain in web database, as normally. Here is the great article describing that approach:

Solution 2: Employ Sitecore Event Queue no notify CM about CD changes, so that as soon CM receives update event, it updates itself with the latest change coming from CD and then re-publishes the change across the rest of CD databases in order to keep them in sync. We describe this approach below.


Sitecore has a mechanism that is called remote events and allows communication between instances. This is implemented via "core" database that has EventQueue table that is monitored by a minor periods of time (like 2 seconds). We always have connection string reference to core database on our CD, at least for authorization / security purpose but also to support Event Queue that servers transport for publishing operations.

So, the process of saving comment on back end now looks fairly complicated - as post request comes - we still save the changes into CD ("web") database, on CM we also add an additional handler to item:saved event that executes custom code with event handler, that updates the same item on master database. And finally, you may programmatically re-publish updated item to other CD instances (if many), that do not have an updated version yet.

Now let's look at the code that implements all described below. AjaxController will include additional code right before returning JSON back to browser:

UpdateCommentEvent evt = new UpdateCommentEvent();
evt.Id = item.ID.ToString();
evt.FieldName = fieldName;
evt.Value = database.GetItem(id).Fields[fieldName].Value;
                 
Sitecore.Eventing.EventManager.QueueEvent<UpdateCommentEvent>(evt); 

What we're doing here is just queueing an event. UpdateCommentEvent is a custom event written by us, it is normal C# class, but please pay attention to DataContract and DataMember attributes. This is required for serialization purposes. Here is how UpdateCommentEvent is defined within the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace Website.Code.Events
{
    [DataContract]
    public class UpdateCommentEvent
    {
        [DataMember]
        public string Value { get; set; }

        [DataMember]
        public string Id { get; set; }

        [DataMember]
        public string FieldName { get; set; }

        public UpdateCommentEvent(string id, string fieldName, string value)
        {
            Value = value;
            Id = Id;
            FieldName = fieldName;
        }

        public UpdateCommentEvent()
        {
        }
    }
}

And, of course, we define UpdateCommentEventArgs that comes along with our new event:

namespace Website.Code.Events
{
    public class UpdateCommentEventArgs : EventArgs, IPassNativeEventArgs
    {
        private UpdateCommentEvent _evt;

        public UpdateCommentEventArgs(UpdateCommentEvent evt)
        {
            _evt = evt;
        }

        public string Id
        {
            get { return _evt.Id; }
        }

        public string FieldName
        {
            get { return _evt.FieldName; }
        }

        public string Value
        {
            get { return _evt.Value; }
        }
    }
}
Implementation on CM side: first of all we need need to specify a new hook.

    
 ...   

Here's the code referenced by hook specified above. We subscribe to our event and once it arrives - we call Run method that arranges local (for CM environment) event:

using System;
using Sitecore.Events.Hooks;
using Sitecore.Eventing;

namespace Website.Code.Events
{
    public class UpdateCommentHook : IHook
    {
        public void Initialize()
        {
            // and now raise event locally
            EventManager.Subscribe<UpdateCommentEvent>(new Action<UpdateCommentEvent>(UpdateCommentEventHandler.Run));
        }
    }
}

We need to specify new local event called updatecomment:remote in the configuration as associate it with a handler method:

    
    
    

And here is OnUpdateCommentRemote event handler that fires on CM. It gets event arguments, casts them to UpdateCommentEventArgs and extracts data out of arguments. In order to update an item on CM we require 3 parameters: Item ID, field name to be updated and the value to be updated with. All three are stored in event arguments so now we are able to update item on master database.

namespace Website.Code.Events
{
    public class UpdateCommentEventHandler
    {
        /// 
        /// The method is the method that you need to implement as you do normally
        /// 
        public virtual void OnUpdateCommentRemote(object sender, EventArgs e)
        {
            if (e is UpdateCommentEventArgs)
            {
                var args = e as UpdateCommentEventArgs;
                var master = Sitecore.Configuration.Factory.GetDatabase("master");

                using (new SecurityDisabler())
                {
                    var itemOnMaster = master.GetItem(args.Id);

                    using (new EditContext(itemOnMaster))
                    {
                        itemOnMaster[args.FieldName] = args.Value;
                    }
                }

// also do publishing to other CD here, if required
} } // This methos is used to raise the local event public static void Run(UpdateCommentEvent evt) { UpdateCommentEventArgs args = new UpdateCommentEventArgs(evt); Event.RaiseEvent("updatecomment:remote", new object[] { args }); } } }
If there are multiple CD environments (at least one apart from the one where we edit the comment) you will also need to re-publish from CM to those CDs in order to keep them all in sync.

So, in these two blog posts we described how to make an ajax MVC call to controller on back-end server and update user editable content on CD environment keeping it in-sync with other environments. Hope this post helps you to understand Sitecore architecture better.

See also (additional references):

Sitecore.Support.RemoteEventLogging
Tweak to log all item:saved:remote events of EventQueue that were just processed.
https://bitbucket.org/sitecoresupport/sitecore.support.remoteeventlogging/wiki/Home

Tip: copying Presentation Details manually

I came across a question on StackOverflow where a guy asked about copying presentation details and decided to share this quick tip. Saying Presentation Details I mean all the information about layouts, renderings, placeholders etc., so whatever you usually configure on that screen:



So, you usual data is stored within item's fields, but where does presentation live? Well, presentation is also kept within item, but in a slightly different location.

You page template is inherited from Standard Template, it has plenty of important fields and sections, among which there is Layout section. Let's go and see what is there. But before, open View tab ensure Standard fields is checked in order to display all sections provided by Standard Template and also check Raw values option to display actual content of the fields:


Then, scroll down to Layouts section and expand it.


Rendering field contains all presentation details, serialized into XML. So now, if you copy them 'as-is' to clipboard and insert to another item - that item will immediately same layout, all renderings in the same order, placeholders etc. You may also copy that across environments, assuming both target and source environment have those layout and renderings.


Note: if you need to copy Presentation Details just within same database, there a nice and quick solution right from the UI:


Hope this helps!

Sitecore xDB Cloud: don't want to mess with xDB? Let Sitecore do that for you!

Just wanted to share one option I recently found out, not many people aware about.

To start with, I am working in a large insurance organisation (which is in much regulated industry) with a pretty complex configuration of load balances, reverse proxies, multiple geographically distributed CD boxes in different data centers (and networks). So I was very surprised to find out there is such an offer from the vendor.

Sitecore offers to host you xDB in their cloud, powered by Azure. All maintenance and processing raw data from Mongo to Reporting database is done on their side. What is ends up for organisation is just setting connection string (to reporting database) in config and ensure firewall rules allow connectivity to the instance.

Pricing seems to be bespoke for your solution and not cheap (as everything from Sitecore), but it is reasonable if compare to a full time resource efforts, for example it comes out that our company even saves a bit! Again, this may not fit to all organisations and depends on people and infrastructure they already have in possession.

This information is a very "early bird" for me, so I will update with more details as soon as we start working with Sitecore xDB Cloud

IIS URL Rewrite module - as reverse proxy with links rewrite

Not many people know that IIS itself can serve as Reverse Proxy, with rewriting URLs on-the-fly. We are going to take a look on how to configure that feature. Let's assume we have 2 websites - primary website that has URL http://test2/ and is a hosted by IIS, moreover there is an instance of Sitecore installed; and another external static website that has URL http://external/ and it has few static pages and resources. For this experiment I got external website hosted at the same IIS instance, while in reality it can be literary anything and anywhere.


Apart from having IIS, you will need the following prerequisite:

- URL Rewrite Module installed, version 2.0

- Application Request Routing version 2.0


The easiest way to get all the prerequisites is to install them through Web Platform Installer. It will install all of them so you'll just need to have IIS refreshed and get ready to start.



External website contains static.html file with the following code

<div>
    img/sitecore.png<br>
    <img src="img/sitecore.png" alt="sitecore" width="230" height="106">
</div>
<div>
    /img/sitecore.png<br>
    <img src="/img/sitecore.png" alt="sitecore" width="230" height="106">
</div>
<div>
    http://external/img/sitecore.png<br>
    <img src="http://external/img/sitecore.png" alt="sitecore" width="230" height="106">
</div>
<p>
    <a href="sitecore.zip">sitecore.zip</a><br>
    <a href="/sitecore.zip">/sitecore.zip</a><br>
    <a href="http://external/sitecore.zip">http://external/sitecore.zip</a><br>
</p>

This code has 3 images and 3 links to an archive file, each of them is either relative link (from the doc level, for sure) or absolute link (from web root) or fully qualified link including domain name and protocol. This HTML renders renders into the following screenshot:


Our objective is to have a "virtual" "folder" called ext on the test2 website so that it "mapped" to external website and also correctly "maps" and rewrites all the resources of external website on resulting page.

Example:

When we hit http://test2/ in browser - we get default Sitecore page as it is provided by Test2 website, as normally.

When we hit http://test/ext/static.html - we get the page at that URL but with the content of external/static.html page with all links and references rewritten to be test2/ext/*.* instead of external/*.*

So, to make IIS Rewrite work as reverse Proxy, let's do the following steps:


Make sure "Enable proxy"is checked, otherwise nothing will work.


In URL Rewrite section, click "Add Rule(s)" link, then from popup screen select "Reverse Proxy" and specify the rule. Also check outbound rules as the are rules that factually rewrite internal links. Please note that this function may add some overhead to your website performance.


After you specify the rules - one inbound and 2 outbound (they are shown below) - reverse proxy now functions and you may verify that by requesting the following ULR (as on the screenshot below):


Notice, that all links and images look correct, as the were before. To ensure they were rewritten correctly, let's view the source file of resulting page. Here is it:

<div>
    img/sitecore.png<br>
    <img src="img/sitecore.png" alt="sitecore" width="230" height="106">
</div>
<div>
    /img/sitecore.png<br>
    <img src="http://test2/ext/img/sitecore.png" alt="sitecore" width="230" height="106">
</div>
<div>
    http://external/img/sitecore.png<br>
    <img src="http://test2/ext/img/sitecore.png" alt="sitecore" width="230" height="106">
</div>
<p>
    <a href="sitecore.zip">sitecore.zip</a><br>
    <a href="http://test2/ext/sitecore.zip">/sitecore.zip</a><br>
    <a href="http://test2/ext/sitecore.zip">http://external/sitecore.zip</a><br>
</p>

As there were no need to rewrite relative URLs - they remain untouched. However root-folder URL and full URL were rewritten to satisfy new domain name and desired folder-path.

And finally, here is resulting configuration that makes it all work. Whatever we have previously configured is stored in the configuration file within system.webserver node in rewrite section:

<rewrite>
      <rules>
        <clear></clear>
          <rule name="ReverseProxyInboundRule2" stopprocessing="true">
            <match url="(ext)/(.*)?"></match>  
              <conditions>
                  <add input="{CACHE_URL}" pattern="^(https?)://"></add>
              </conditions>
              <action type="Rewrite" url="{C:1}://external/{R:2}"></action>
          </rule>
      </rules>
      <outboundrules>
        <rule name="ReverseProxyOutboundRule2" precondition="ResponseIsHtml1">
          <match filterbytags="A, Form, Img" pattern="^/(.*)" negate="false"></match>
          <action type="Rewrite" value="http://test2/ext/{R:1}"></action>
        </rule>
        <rule name="ReverseProxyOutboundRule1" precondition="ResponseIsHtml1">
          <match filterbytags="A, Form, Img" pattern="^http://external/(.*)?" negate="false"></match>
          <action type="Rewrite" value="http://test2/ext/{R:1}"></action>
        </rule>
        <preconditions>
              <precondition name="ResponseIsHtml1">
                  <add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html"></add>
              </precondition>
          </preconditions>
      </outboundrules>
    </rewrite>

There is no need to use visual configurer at all, you may just drop this snippet on web.config into appropriate section and it will start working straight away!


Sitecore 8: re-indexing errors out and module installation never ends without MongoDB running

What is happening? we got a commonly met point of frustration since recent - imagine, you have just installed an instance of Sitecore 8 and are trying to install some useful modules, for example Web Forms for Marketers 8.0 or PowerShell Module. And all you get is never-ending progress box dialog.


However that occurs not only while installing a module, but also when trying to rebuild indexes via built-in Developer toolbar interface. Same story, but at least this time it tries to tell us something with View all messages section, unfortunately unsuccessfully - there are no any error messages seen once you expand this box.



Why is it happening? Going through log files made me thinking there is something with xDB, it looks like sitecore tries to perform write operation into Mongo, but is not able to do. And because Sitecore 8 is now using modern client-based SPEAK interface instead of outdated SheerUI, the back-end where in fact an error occurs is not able to notify client about that (I believe is it not yet implemented and would be fixed with future updates).

How to fix? Let's install and run MondoDB. After default windows installation, the easiest way of running Mongo would be just running its server with dbpath parameter to where DB placed. I say the easiest because there is a better alternative to run MongoDB as Windows service application, so that it will run on system start up.


So, as soon Mongo is up and running, let's test our assumption and try to re-build Lucene indexes again:


And bingo! It now works well! Hope this solution helps.

Sitecore extensions for Google Chrome review

  1. Sitecore Developer Tool
  2. Sitecore Analytics Testing Tools
  3. Sitecore Expand Collapse Sections
  4. Sitecore Keyboard Shortcuts
  5. Sitecore Helper
  6. Dan's Sitecore Shortcuts

1. Sitecore Developer Tool

This is a nice, elegant and non-obtrusive shortcut extension located at the top right of your Chrome browser. It has several most useful shortcuts logically grouped by tabs.


Admin Pages tab contains useful admin pages hotlinks.


Database tab allows to quickly change context database.


Mode tab has 6 switchers - the names are self-describing.


There is also options tab, where you can add / edit favourites, add more databases and perform other settings for the extension.

You may install Sitecore Developer Tool by this link.



2. Sitecore Analytics testing tools

As it comes clear from its name, this is an extension to fit specific analytics purrposes, which are: clearning analytics-related cookies and specifying a forwarded IP address for GeoIP lookups.


Below there are screenshots of its settings screen:





Download and install exension: the link.



3. Sitecore Expand Collapse Sections

Minimal extension that serves just one purpose - expand and collapse data section panels in Sitecore.


What can be easier?


Unfortunately, at the moment this extension does not support Sitecore 8, so the last funcional verison is 7.5

You may install Sitecore Expand Collapse Sections by this link.



4. Sitecore Keyboard Shortcuts

It presents


It is a powerfull extension that allows you to create hot keys to quickly complete common tasks in Sitecore. This is especially useful for demos or for quick access to frequently used items. No need to repeatedly expand the content tree any longer.

it works well for Sitecore 8 as well as with all previous versions I have tested with. It also works well on Mac computers, however mac-specific keyboard extensions (ie. Cmd) are not supported. screenshot below shows settings screen:


And here is a dropdown containing list of possible actions. Quite impressive!


Download and install exension: the link.



5. Sitecore Helper

This extension brings upgrades the Sitecore interface with toggleable usability fixes.Applies several fixes & updates to the Sitecore user interface, use the options menu to toggle them on / off.


Download and install exension: the link.




6. Dan's Sitecore shortcuts

Extension provides a drop down with features and shotcut buttons.


Download and install: the link.


Hope these extensions may help you to improve your productivity while working on Stecore projects!

Know your tools: SIM - Sitecore Instance Manager

Sitecore Instance Manager (SIM) - the must-have tool for all Sitecore professionals and platform enthusiasts. It is a "Swiss army knife" for all types of activities related to installation and configuring Sitecore instances. So, what it does?

As it is obvious from its title, SIM simplifies installation of Sitecore, minimizing it to just few very intuitive clicks. SIM supports all versions of Sitecore, developers work tightly with platform vendor, so since recent they tend to synchronize SIM updates with new Sitecore releases. Oh, nearly forgot to mention, SIM has auto-update module that can update the program silently in background, or with a prompt, or just leave user alone once he prefers getting updates donу manually.

Here is the main screen of Sitecore Instance Manager:


You have all available instances listed, you can install new or remove existing, do some configuration changes and much more. SIM operates "web-folder" installation archives as they came form Sitecore, one can download zip and manually place it into specific folder (that is configurable in program settings) or can download and store any platform version directly from SDN. In that case he/she might need to type in SDN credentials and pick up exact Sitecore version from options drop-down. As soon as zip is downloaded, it can be installed.

The installation process occurs in few clicks and is show on several screenshots below. First of all, we select which version we are going to install from the list of stored in local repository. Also there are fields to specify instance name, hostname and the installation folder.


The program accurately installs files, restores database and sets appropriate SQL permissions, configures Application Pool and create config files with correct values.

SIM is great in that it allows not just install Sitecore itself, but also you may specify which modules you would like to install straight away, just by simply checking them from the list of available.



Apart from modules you may also install certain custom packages, likewise you may have a fully working website - both items and file system substructure packed within a package, so it may become available straight after the installation. As another example, I always install useful Sitecore adjustments with SIM in order to benefit out of them straight away.



Not only custom packages can be auto-installed, but also such called configuration presets. These are certain configuration patches, each addressing small but important setting, will be placed into App_Config/Include folder.



The installation itself does not take much time. Sitecore 8 takes approximately 1 minute in virtual machine on my MacBookPro. Significantly faster comparing with time spent on default installer.




SIM also have multiple useful shortcuts at one place, like links to important Sitecore folders, configuration tools, hosts editor, IIS recycle an many many more.




I would award SIM with the highest rate and highly advise to download and play with it, even if you do not regularly play with installation and instances.

Download: SIM on Sitecore Marketplace