Quick Pro Tip: Running low on disk space? Delete your hiberfil.sys
Friday, May 18, 2012 3:03:48 PM (GMT Daylight Time, UTC+01:00)
I freed up about 10GB of space on my C drive this afternoon by simply disabling Hibernation:
Before

After

Step 1: Open Command Prompt as an Administrator
Step 2: Type powercfg -h off and hit enter -if nothing else is output then you've done it right:

Step 3: Marvel at your additional disk space
That's it.
The hiberfil.sys file is a (large) file which stores the the current state (memory) of your computer. You can't just hit delete on it as it's always in use but you can free up a lot of space quickly by disabling Hibernate (you might as well if you never use it):
Umbraco powered CheckBoxList or DropdownList nodes in uCommerce admin area
Saturday, May 12, 2012 2:53:24 PM (GMT Daylight Time, UTC+01:00)
uCommerce is a great e-commerce engine for Umbraco and high on our list of options when evaluating new e-commerce projects at The Site Doctor.
One thing that has always bugged me however is the lack of extensibility on the admin backend. This is something I've discussed with Søren in the past and I believe is on the cards to be resolved in the upcoming v3 release but we wanted to see if there was a way we could get it working in the current release. Thanks to Dan's digging we've found it is (ish).
Skip to the downloads.
A Little Background
Before I dive into the code and overview how you should get it all wired up, I think it's worth understanding how uCommerce is structured out of the box:
- Each uCommerce section has it's own folder under the "umbraco/uCommerce" folder so if you want to extend the functionality for the "Product Catalog" area then you will need to look in the "Catalog" folder.
- The ASPX files are the main containers (e.g. EditProduct.aspx) and don't generally include any logic as they load up the various UserControls (the ASCX files) as tabs.
- Adding custom tabs can be done through the uCommerce_AdminTab table but that's outside the scope of this post but Lasse has a good introduction on his blog.
As we plan to alter the product details edit functionality, we'll need to make a few changes to EditProductBaseProperties.ascx. It's worth taking a quick look around the file if you've not before as we've re-ordered the boxes, added classes and hidden the SKU field before, all of which is often useful. You'll notice at the end of the file there's a repeater "ProductPropertyRepeater" -this is where uCommerce outputs the various custom properties you've setup.
Why's it useful?
Have you ever felt limited by the options of Text/Boolean/Enum/Image/Number and RichText and wished you could implement your own cool control like an image cropper or checkbox list? What about driving that control with data from Umbraco or another data source? If you have, this is the blog post for you as it handles all those scenarios.
Getting Started
If you don't want to know how to do this yourself you can click here to skip to the downloads and get started straight away.
Caution: If you want a CheckBoxList or your own custom control, you need to be running v2.6 as it adds IWebControlAdapter. As of v2.6 I'm not convinced IWebControlAdapter is fully implemented as it allows you to retrieve the value from the control but we will need to override the UserControl which outputs the control which feels hacky.
Create a custom control
Unless you want a fairly standard control (TextBox, CheckBox, DropDownList or ContentPickerUCommerce) you'll need to create a control which uses the IWebControlAdapter interface. This will mean uCommerce is able to get the value from it. You will need to implement two methods:
- Adapts(Control) - allows you to specify whether it is a control you will be managing.
- GetValue(Object) - allows you to specify what value the control contains In it's entirety it could be pretty simple:
namespace TheSiteDoctor.Web.uCommerce.Admin.UI
{
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Linq;
using UCommerce.Presentation.Web.Controls;
public class UCommerceUmbracoDrivenCheckboxList : CheckBoxList, IWebControlAdapter
{
public bool Adapts(Control control)
{
return control is UCommerceUmbracoDrivenCheckboxList;
}
public object GetValue(object control)
{
var li = (UCommerceUmbracoDrivenCheckboxList)control;
var values = (from ListItem listitem in li.Items where listitem.Selected select listitem.Value).ToList();
return string.Join(",", values);
}
}
}
Create your own DataTypeControlFactory
uCommerce uses DataTypeControlFactory to determine what control to render in the editor so you'll need to override the default implementation because it doesn't look for IWebControlAdapters at the moment.
This is simple enough, it needs to accept a Page (the page you'll be adding the control to) and the work out what control it should output e.g.:
public class CustomDataTypeControlFactory : DataTypeControlFactory
{
Page _page;
public CustomDataTypeControlFactory(Page page)
: base(page)
{
_page = page;
}
public Control CreateCustomControl(IProductProperty productProperty)
{
if (productProperty == null)
throw new ArgumentNullException("productProperty");
return CreateCustomControl(productProperty.ProductDefinitionField, productProperty.Value);
}
private Control CreateCustomControl(ProductDefinitionField productDefinitionField, string value)
{
var fieldType = productDefinitionField.DataType.TypeName;
if (fieldType.StartsWith("Something"))
return base.CreateControl(productDefinitionField, value);
// Some logic to work out whether you should be creating the control
return customControl;
}
}
Add a custom ProductPropertyEditor.ascx
Now you'll need to create your own ProductPropertyEditor.ascx which calls your new DataTypeControlFactory:
public partial class CustomUcommerceProductPropertyEditor : ProductPropertyEditor
{
protected new void Page_Load(object sender, EventArgs e)
{
InitializeControl();
}
public override void DataBind()
{
base.DataBind();
}
private void InitializeControl()
{
Controls.Clear();
Control child = CreateControlToRender();
Controls.Add(child);
}
private Control CreateControlToRender()
{
if (ProductProperty == null)
{
throw new InvalidOperationException("Cannot create control, ProductProperty not set.");
}
var factory = new CustomDataTypeControlFactory(Page);
return factory.CreateCustomControl(ProductProperty);
}
}
You may notice that we're clearing the controls in the InitializeControl which is called from Page_Load whereas uCommerce makes the same call from DataBind(); this is intentional. I've not yet figured out why but if you don't do it from Page_Load, uCommerce still outputs a TextBox.
Wire it all up
To get this all outputting, you now need to replace the call to ProductPropertyEditor in EditProductBaseProperties.ascx with a reference to your own ASCX file, you can do this by updating this line:
<%@ Register Src="~/Umbraco/UCommerce/Controls/ProductPropertyEditor.ascx" TagPrefix="commerce" TagName="ProductPropertyEditor" %>
To:
<%@ Register Src="~/Umbraco/UCommerce/Controls/[YourFileNameHere].ascx" TagPrefix="commerce" TagName="ProductPropertyEditor" %>
Edit your Presenters.config
At this point if you were to load up your uCommerce backend you'd find that it outputs as intended but when you saved the file you'd either get an error or the value doesn't save. The reason for this is although you've replaced the display aspect of the editor, uCommerce is still looking at it's old "GetValue" implementation instead of your new control.
Open /umbraco/ucommerce/configuration/presenters.config and add a reference to your control which implements the IWebControlAdapters interface:
<component id="UCommerceUmbracoDrivenCheckboxList"
service="UCommerce.Presentation.Web.Controls.IWebControlAdapter, UCommerce.Presentation"
type="Your.Custom.uCommerce.Namespace.ControlName, Your.Custom.uCommerce.AssemblyName"
lifestyle="PerWebRequest"/>
That's it, you're done, you can now add your own controls to the backend of uCommerce.

Download
To make life easier I've packaged the files up into an Umbraco package and have also added the source:
Download the source code with references (C#) - 800KB
Download the Umbraco Package - 12KB
Instructions on using the package
If you download the source code you may have noticed we're doing some funky things with the DataTypeName. The idea behind the control is that it allows us to output a DropDownList or CheckBoxList containing values based on a Document Type and start node purely from the name of the DataType.
I can go into more detail about how we're doing this if it's of interest, just leave me a comment below but to start using it straight away you will need to use the following naming convention for your DataType's name:
- TSD_: This is the prefix we use to identify whether it's a control we should be handling
- Control Type: Currently this can either be "ddl" for a DropDownList or "chkl" for a CheckBoxList
- Start Node Id: This should be the id of the Umbraco parent node. You can cheat and use the root node but it's best to use the parent
- Document Type: This is the Document Type's alias to use
Example: if you wanted a list of checkboxes for ShapeType (as above) then your name would be: TSD_chkl_1234_ShapeType and a DropDownList would be: TSD_ddl_1234_ShapeType
What next?
This is something we're using more and more in uCommerce these days as it allows us to use the power of Umbraco to power uCommerce which is allowing us to do some really interesting things. Although we'll only be developing it as we need it at the moment we do have plans to add support for:
- Radio Button Lists
- Image Cropper
- Adding nesting to the list of items (to offer better support for hierarchical data)
- Powering it by the members and media sections
Quick pro tip - Open File in Sublime from Visual Studio
Tuesday, April 17, 2012 4:58:45 PM (GMT Daylight Time, UTC+01:00)
This is one of those really simple little gems that helps productivity ten-fold. Firstly, regardless of whether you're running a PC, Mac or Linux, if you've not already come across Sublime Text 2 you should go download it right now.
There are a ton of amazing features in Sublime (enough to warrant an talk by Dan Kendall no less), some of them are available in Visual Studio but none are quite as rich. Things like the multi-selection/multi-cursor feature are just awesome for quickly making the same changes in multiple places, not to mention the "select this word throughout the document" shortcut which has saved me hours already.
So what's my quick tip?
Rather than copying/pasting the path in the open dialog each time you want to edit the file, Dan pointed out to me the other day that you can easily wire it up as an external tool and then add a shortcut so you can open the current file in a flash.
Here's a step-by-step guide:


Optional Additional Enhancement
You can wire up a keyboard shortcut for extra speed, again under the "Tools" menu, go to "Options" and then "Keyboard" (under "Environment"):



Thanks Dan for the heads up on this one.
How to generate customer purchase cohorts from uCommerce data
Friday, April 06, 2012 10:02:29 AM (GMT Daylight Time, UTC+01:00)
I've had a couple of people ask how they can create customer purchase cohorts from their uCommerce data since my last post so here's a quick script.
Depending on how you've setup your uCommerce store, the customer ids might be different so instead of using customer id. I would use the email address of the customer personally as the identifier as this means you'll be able to analyse those customers who have chosen to check out anonymously 
Here's the SQL to output the data in a format suitable for www.quickcohort.com.
WITH Actions (FirstAction, LastAction, UniqueId)
AS (
SELECT min(dateadd(dd, datediff(dd, 0, o.CompletedDate), 0))
, max(dateadd(dd, datediff(dd, 0, o.CompletedDate), 0))
, ltrim(rtrim(LOWER(cc.EmailAddress)))
FROM [uCommerce_PurchaseOrder] o LEFT JOIN uCommerce_Customer cc ON cc.CustomerId = o.CustomerId
GROUP BY ltrim(rtrim(LOWER(cc.EmailAddress)))
)
SELECT a.[FirstAction]
, a.[LastAction]
, count(a.[UniqueId]) AS [CountOfCustomers]
FROM
Actions a
GROUP BY a.[FirstAction]
, a.[LastAction]
HAVING
min(dateadd(dd, datediff(dd, 0, a.[FirstAction]), 0)) IS NOT NULL
ORDER BY a.[FirstAction]
, a.[LastAction]
GO
Not using uCommerce as your e-commerce provider? Let me know and I'll knock up a script for you.
How to generate the data needed for a cohort chart- cohort analysis Part 3
Wednesday, February 22, 2012 1:15:53 PM (GMT Standard Time, UTC+00:00)
Sean Ronan pointed out on twitter that "what" the columns on www.quickcohort.com are which is a good point so I thought I would fill in the gaps a little. The data for a cohort is fairly simple and can be as granular as you decide but the columns needed are:
- First Action Date/Time
- Most Recent Action Date/Time
- Count of customers which have this First/Most Recent Action Date/Times
Most of the time you can strip the time part from the date/time (especially if you're looking at the data on a month basis) but the tricky part is getting the count of users within each date grouping. You can't just select min/max dates as you need the data grouped by your unique customer identifier. If you're running SQL Server 2005+ then you've got the benefit of Common Table Expressions.
For this example, I've assumed a simple order table structure which contains a Customer Reference (CustomerId) and an Order Date (OrderDate). You could however use any date and identifier which groups actions together e.g. ProfileId and LastLoginDate.
SQL 2005 or later
WITH Actions (FirstAction, LastAction, UniqueId)
AS (
SELECT min(dateadd(dd, datediff(dd, 0, o.[OrderDate]), 0))
, max(dateadd(dd, datediff(dd, 0, o.[OrderDate]), 0))
, o.[CustomerId]
FROM Orders o
GROUP BY CustomerId
)
SELECT a.[FirstAction]
, a.[LastAction]
, count(a.[UniqueId]) AS [CountOfCustomers]
FROM
Actions a
GROUP BY a.[FirstAction]
, a.[LastAction]
ORDER BY a.[FirstAction]
, a.[LastAction]
Otherwise, I think you'll need to write something using temporary tables e.g.:
Pre SQL 2005
CREATE TABLE #Actions(
FirstAction SMALLDATETIME,
LastAction SMALLDATETIME,
UniqueId INT
)
INSERT INTO #Actions
(
FirstAction
,LastAction
,UniqueId
)
SELECT min(dateadd(dd, datediff(dd, 0, o.[OrderDate]), 0))
, max(dateadd(dd, datediff(dd, 0, o.[OrderDate]), 0))
, o.[CustomerId]
FROM #Orders o
GROUP BY CustomerId
SELECT a.[FirstAction]
, a.[LastAction]
, count(a.[UniqueId]) AS [CountOfCustomers]
FROM
#Actions a
GROUP BY a.[FirstAction]
, a.[LastAction]
ORDER BY a.[FirstAction]
, a.[LastAction]
DROP TABLE #Actions
This should then generate some data that looks like this:
| FirstAction |
LastAction |
CountOfCustomers |
| 2011-03-01 |
2011-03-01 |
1 |
| 2011-03-01 |
2011-04-01 |
1 |
| 2011-04-01 |
2011-06-01 |
1 |
| 2011-05-01 |
2011-10-01 |
1 |
| 2011-06-01 |
2011-11-01 |
1 |
| 2011-07-01 |
2011-08-01 |
1 |
| 2011-08-01 |
2011-08-01 |
1 |
| 2011-09-01 |
2011-12-01 |
1 |
| 2011-10-01 |
2012-02-01 |
1 |
| 2011-11-01 |
2012-02-01 |
1 |
| 2011-12-01 |
2012-02-01 |
1 |
| 2012-01-01 |
2012-01-01 |
1 |
| 2012-02-01 |
2012-02-01 |
2 |
Which you should just be able to drop into www.quickcohort.com. I've not written a version for MySQL as I suspect someone far better at MySQL will be able to pop something together but the pre SQL 2005 script should work.
How to read and interpret a cohort chart - cohort analysis Part 2
Monday, February 20, 2012 2:31:06 PM (GMT Standard Time, UTC+00:00)
In my last post about Cohort analysis I briefly introduced what a cohort graph is, in this one I'm going to go into a little more detail about how you can use it. I'm planning on releasing some code so you can add it to your reporting suite in another post but if you can't wait until then, I've thrown up a quick online cohort generator at: www.quickcohort.com -just dump your data into it and click graph.
What can the cohort chart tell us?
A cohort chart can give you an idea of customer loyalty -and- an indication of potential problems in their lifecycle.
The key metric it gives us is customer loyalty. This is also one of the most frequently ignored metrics in every company (mostly because people aren't clear on how to measure it) yet it's probably one of the most important at the same time.
What is Customer Loyalty?
That depends very much on your business and as a result the graphs will likely look very different. It's probably easiest to compare what a couple of business types -a retailer and an online magazine- might consider loyalty.
| Retailer | Magazine |
- Repeat sales
- Visits to the store
- Engagement with the store e.g. email opens
| - Multiple Logins
- Subscription
- Number of comments on an article
|
As you can see, although there may be some cross overs (Visits to the store and Logins are probably quantified by a similar metric) the "what" depends very much on your business but the longer the duration between the first and last engagement in all scenarios above is how you can demonstrate loyalty.
How should I read a cohort chart?
Unlike most tables which you read left to right (a row at a time), you'll probably get more value out of a cohort chart from reading it by a column at a time. This will enable you to spot possible problems in the user's lifecycle.
Problem points in the user's lifecycle can often be spotted where the colours change in the same column. The greater the difference between the shades of colour, the bigger difference is between the two months. In a perfect world, 100% of the customers from Month 0 will still be using your site in Month 12 but life is rarely like that.
Looking at the chart below of user logins over time, the eager should spot that there are three months which appear to have issues: Month 3, Month 5 and Month 9:

If you're not sure on what you're looking for, you're spotting those columns which have a similarly shaded background which then lightens in the next month (or in the case of Month 9 is completely unshaded.
What does this mean?
As the chart above was logins over time, a quick glance over this cohort chart this would suggest the following to me:
- Overall there is a pretty good retention for the users (the percentage of customers going from month to month are pretty high)
- The majority of users only remain engaged up to month 3, at which point a lot of users lose interest in the site
- There is a clear drop off in customer retention after month 9
- The eldest customers had the best retention rates (despite the drop off in Month 9, 30-40% of visitors were still coming back until then)
When reading a cohort chart, you can generally discount the last cell of each cohort as it's the current month.
What might the cohort chart look like?
Repeat Sales
Logically, to be a repeat customer, you have to make at least two purchase from the retailer so the duration we're interested in (the month) is the period between the first purchase and most recent purchase.
First Date (Month 0): First Purchase
Last Date (Month x): Most Recent Purchase
Check out the cohort chart below and see what you can interpret.
Remember, Month 0 represents the first purchase -all customers appear in this column. Looking over the cohort chart, of the 69 customers the retailer had in October 2010, 39% (27 customers) were still around in November 2010 (month 1), 21% (15 customers) were still around in December 2010 and so on.
None of the 69 customers who made their first purchase in October 2010 are still a customer 12 months later (although if your customers tend to make a purchase near the end of the month, the customer who made a purchase in September 2011 may still make a purchase).
So looking at that chart, 30-40% of customers would make a second purchase 2 months after their first purchase. Of the older customers (those which first purchased before March 2011) 10% would make another purchase 5-6 months later.
What else can we gleam?
There are a few interesting things with the chart above, another is the sudden drop off in month 3 for those customers who first purchased in May/Jun 2011, similarly, the customers who signed up in Mar/Apr 2011 also stumbled in the same month, that could indicate some form of seasonal trend or change in marketing.
With a little background you will be able to get a much better insight into the meaning behind some of the numbers. If for instance you had changed your marketing routine around Jun 2011 this might explain the difference in numbers. It might be that your business is very seasonal (in which as you'd be better to look at a 24 month chart rather than 12 month).
Keeping a record of what you were doing around the different months is important, for instance you might start a pay-per-click campaign. Everyone's happy because you notice an increase in sales (so an increase in Month 0) but are they a valuable customer (a repeat purchaser) or a one-off? Cohort chart analysis will quickly highlight this to you as the increase in Month 0 will be reflected in Month 1.
Although time will tell, it would appear that a lot of customers make another purchase about 2 months after their first. This could be co-incidence or it might be that you're selling a product with a small sample accessory (e.g. a free pack of chalk with each chalkboard) and that sample pack runs out after a couple of months. Alternatively it could be a fault in the product e.g. you sell hinges and the oil runs out after a couple of months so they're buying more grease. By adding a little context to the data you'll likely get even more interesting stats out (we certainly have in the past).
Visits to the store or customer engagement
Things start to get really interesting when you start comparing two cohort charts for the same customer base and period against each other.
First Date (Month 0): First Purchase
Last Date (Month x): Most Recent Login
What's interesting when you compare the two charts is most of the customers who have made a purchase are still returning to the site (over 30% are still logging in in Month 11). This would suggest there's not as greater a problem with customer retention as there is with sales.
This could be because your store sells seasonal products but you keep customers engaged, it might just be a co-incidence but it should drive investigation.
![Cohort-Created-To-Last-Purchse[3] Cohort-Created-To-Last-Purchse[3]](http://blogs.thesitedoctor.co.uk/tim/images/How-to-read-and-interpret-a-Cohort-Trian_FC17/Cohort-Created-To-Last-Purchse3_thumb.png)
What Can I do with this information?
In isolation it's helpful but only really gives you a top level view on a customer's lifetime with you, it's when you're able to combine this data with knowledge of your business, sales statistics, marketing strategy and information such as a customer's LTV (Lifetime Value) that it gets really interesting and useful.
Using a cohort chart and average sale value, you can use it as part of your sales forecasting and predict what your company's sales will be going forwards e.g.: if the Average Order Value is £10 and your average first 5 months looked like this:
| Month: | 0 | 1 | 2 | 3 | 4 |
| Customer Trend | 1,000 | 500 | 200 | 100 | 10 |
You'll then know that of the 1,000 or so customers which sign up in the current month your revenue is likely to look something like this:
| Month: | 0 | 1 | 2 | 3 | 4 |
| Expected Sales | £10,000 | £3,000 | £1,000 | £900 | £100 |
How have I got to those numbers? Well, of the 1,000 customers that purchase this month, 50% will make another purchase on or after month 1 and 20% will make a purchase on or after month 2. With this in mind, of the 1,000 customers from Month 0, 30% will make a purchase in Month 1 so the calculation is as follows:
([Number of customers] * [Percentage Returning in Month]) * [Average Order Value] = [Expected Sales]
(1,000 * 30%) * £10 = £3,000
There are a few assumptions with doing it like this -for instance, customers who purchase monthly will only be counted once etc but this is still a good start.
Using a cohort chart with sales data becomes very powerful as you're able to get a really good insight into whether campaigns are generating worthwhile leads or just generating traffic to the sites. If you're interested in reading more about that I'll overview it in another post as that gets pretty heavy on number crunching.
If you find that customers tend to drop off after a set number of months then it might be worth setting up some form of customer engagement which is triggered just before this point e.g. an email upselling a product that complements theirs or asking them to get in touch with feedback.
Avoid Unrecognized attribute 'xmlns:xdt' configuration error
Friday, February 10, 2012 1:21:02 PM (GMT Standard Time, UTC+00:00)
If you've been using web.config transformations or a nuget package with transformations in at all you'll no doubt have come across the runtime configuration error of "Unrecognized attribute 'xmlns:xdt'. Note that attribute names are case-sensitive" pointing at the start of the <configuration> node (if not see below). This one has been bugging me for a while so I thought I'd work a way around it.
Server Error in '/' Application.
Configuration Error
Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.
Parser Error Message: Unrecognized attribute 'xmlns:xdt'. Note that attribute names are case-sensitive.
Source Error:
Line 1: <?xml version="1.0" encoding="utf-8"?>
Line 2: <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
Line 3: <configSections>
Line 4: <section name="Exceptioneer" type="Something, Assembly" requirePermission="true" />
Source File: c:\domain.com\web.config Line: 2
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.272
The Solution
To work around this, I've assumed you're running some form of custom build already (or have access to add a pre-deploy build step).
- Download and install the MSBuild Community Tasks
- Add the new FileUpdate build step below which looks at your config file (you'll need to run this against each one). You'll also need to have a space in the ReplacementText as you can't currently replace with String.Empty.
- You're done
Simple but effective I think 
<ItemGroup>
<FileUpdateFiles Include="**\*.config" />
</ItemGroup>
<FileUpdate
Files="@(FileUpdateFiles)"
Regex=" xmlns\:xdt\="http\://schemas\.microsoft\.com/XML-Document-Transform""
ReplacementText=" " />
If you've got a better (automated) solution, please let me know, it's been bugging me for ages!
There’s never a right time
Friday, January 27, 2012 8:12:00 AM (GMT Standard Time, UTC+00:00)
I'm about to go on holiday (yay me!), but have you ever worried beforehand because there's not enough time to complete everything you need to get done before you go?
Lets face it:-
There's rarely enough time to do everything in the average week when you've got a full week; without meetings or interruptions -let alone when you've got a deadline like a holiday!
So relax; make the deadline work for you. Use it as a way to get things done with a little more focus and clarity. Identify what needs to be done before you go and what can wait for your return and then prioritise what needs to be done. If you get it all done -great. If not, I suspect that it can wait for your return so enjoy yourself knowing that you'll get stuck in on your return.
Down time is important for everyone, if your clients worry about you going away, you can reassure them that when you get back you'll be raring to go.
Shameless plug - if you are thinking about going on holiday -but worried about supporting your clients, you should check out Crisis Cover. Crisis Cover allows you to store important information such as usernames and passwords and give on-demand (and audited) access to anyone supporting your clients in your absence.
You can sign up as an supplier here