Had a great time presenting my WCM tips presentation over the last week or so - first to a record attendance at the UK SharePoint user group (we hit 200+ attendees for the first time!) and then to a Gold partner audience at Microsoft today. I'd like to think the user group record was due in part to the great agenda, but suspect it was really down to the draw of free beer, pizza and curry from LBi ;-) Instead of posting a simple link to the slides, I want to run through the info here because:
- I can attempt to convey some of the info which was in my demos here.
- I know how many of you will read a blog post but not follow a link to a PowerPoint deck!
First off, this presentation isn't about discussing the standard (but critical) WCM topics which you can easily find quality information on these days. Personally I think if you're embarking on a WCM project your starting point for information should be Andrew Connell's book (and his blog articles if you don't already subscribe), and reading in detail some of the key MSDN articles which have been published on the subject (I list my favorite resources at the end). In these places, you'll find discussion on some of the sub-topics (non-exhaustive list) which I see as the bedrock of WCM:
- Security
- Accessibility
- Optimization
- Deployment
So instead of covering these in detail, my tips focus on key techniques I've found to be powerful in delivering successful WCM projects. They won't be suitable for all WCM projects, but hopefully they give some food for thought and provide some extra value to the WCM info already out there.
Tip #1 - Implement HTML markup in user controls, not page layouts in SPD
Explanation:
Instead of adding your page layout HTML to the page layout in SPD, create a 'parent' user control for each layout which then contains the HTML and child user controls for this layout. This means you have a 1-to-1 mapping between page layouts in SPD to 'parent' user controls within your VS web project. These parent user controls contain the actual markup and also any child user controls used on your page.
Slide bullets:
- Faster development experience - less SPD pain [instantaneous save rather than 3-5 second save plus occasional unwanted changes to markup]
- Page layout markup now in primary source control [alongside your other code]
- Much simpler deployment of updates [simply XCOPY user controls when you deploy updates to other environments]
What it looks like:
So we see that our page layouts now have very little markup (note the ASP ContentPlaceholder controls must stay at the top level, so our parent user control gets referenced in PlaceholderMain):
..and then all of our real markup code is in the parent user control and child user controls which it references:
Tip #2 - create custom IIS virtual directory pointing to your web project files
Explanation:
Most project teams I see put their user controls under 12/CONTROLTEMPLATES (often in a sub-folder) so as to follow what SharePoint does. This is mandatory in some techniques (e.g. custom field controls, delegate controls), but is not required for the type of WCM page user controls discussed in tip #1, and there are arguments for not storing those in the 12 hive. In summary, having a custom IIS virtual directory pointing to your VS project means we avoid having separate design-time and run-time locations for the project files.
Slide bullets:
- Store user controls/page furniture files (e.g. image/XSL) here. Remove code files (e.g. .cs files) for non-dev environments
- Faster development experience – no files to copy, no post-build events. Just save and F5!
- Important if using tip #1 – don’t want to have to compile project [for post-build event to copy files] just for a HTML change
What it looks like:
In our case we created a virtual directory with an alias of 'MIW' (the name of our website) which points to our project files:
All our user control/page furniture file paths then look like '/MIW/PageLayoutsControls/foo.ascx' etc.
Tip #3 - make life easier for the site authors/admins [reduce their stress and they'll be on your side]
Explanation:
This one is a non-technical tip I wanted to throw in there - whilst we're busy getting the front-end of the website right, I think it pays to also think about how authors/admins will use the back-end of the website (N.B. here I mean 'business admin' rather than IT pro). Although this is probably verging on the 'political' side of things, I'd advocate making their life as easy as possible - they often have a loud voice within the client organisation, and if they have bad feedback on what you're building that's a black mark against you.
Slide bullets:
- Consider providing custom tools if the ‘SharePoint way’ is not simple enough (e.g. user management)
- If you use custom lists for site data, provide a link for authors to find them (e.g. using CustomAction)
- Remember these people are rarely SharePoint gurus!
What it looks like:
Clearly this will look different for every project, but for our client we created a custom Site Actions sub-menu with some key links:
This sub-menu provides navigation to a couple of key lists used to power the site, but also to some custom screens we created for user management. Here, we also did some work to simplify things by wrapping up the relatively complex process of creating a user in the membership provider, adding them to various security groups across 2 sites (we had 2 'sister' MOSS sites with single sign-on) and setting certain profile fields, into some simple screens which give a convenient summary of what just happened after creating a new user:
Finally, the 'edit profile' screens used by business administrators were adapted from those used by end users, so that the admins became very familiar with each step of the 'profile wizard' and were better able to support their users.
Tip #4 - plan for unexpected errors
This is an interesting area, partly because we're talking about that category of things which 'should never happen', but sometimes do. Having this conversation with a client (or non-technical management within your own organization) can be fun because the typical response is "whaddya mean the website going to go wrong?", but anyone familiar with software development principles knows there is no such thing as bug-free software. So the important thing is how do we handle these cases when they do occur.
There are several tips here, so I'll break them down into 4.1, 4.2 and 4.3:
Tip #4.1 - implement 'friendly' pages for 404s and unhandled errors
Explanation:
In brief, users of a public-facing website should never see a .Net error if something goes wrong on the website! If this ever does happen, the user is left with the feeling that your website is unreliable and can lose confidence in the organisation - think about it, do you ever remember seeing this kind of error on amazon.com/eBay.com/microsoft.com?
Slide bullets:
- Typically use custom HTTP module to override SharePoint's default error-handling behaviour, checking for:
- HttpContext.Current.Server.GetLastError()
- HttpContext.Current.Response.StatusCode = 404
What it looks like:
On the Standard Chartered site, our 'friendly' message looks like:
Tip #4.2 - implement e-mail notifications to developers for errors
Explanation:
Sorting the user experience is one thing, but what about actually fixing the source of the problem? A key element of this is being alerted whenever a user does experience a problem, rather than relying on them reporting it. When I first told the team we'd be implementing this, I was really thinking about the UAT phase, and also that critical week or two after go-live when you'll occasionally discover some latent issue which had managed to hide itself throughout all the testing. However, what we found is that we got even more value from this in the earlier dev/testing phases. At LBi, the test team sit near the development team, so when the Outlook 'toast' popped up with an exception message which wasn't 'known' by the team, I'd use the information in the e-mail to work out which tester triggered the error, then armed with the stack trace and other information, shout over and ask exactly what they had done to arrive at the problem. Much more efficient than waiting for a full bug report at the end of the day/week!
Slide bullets:
- Means an error cannot happen without the team being aware
- We built this for production, but was even more useful in dev/testing!
- Implemented in same custom HTTP module as error page redirection
What it looks like:
Tip #4.3 - implement proper tracing in your code
Explanation:
So being alerted about unhandled exceptions is one thing, but the stack trace and other details in the e-mail are often not enough information to actually fix the bug easily. This can be because we can't see exactly what happened in the code (e.g. values of variables) leading up to the error. The only real way to obtain this information from production servers (or anywhere where we can't use the debugger) is to add trace/log statements to your code, which when enabled will write out these details as the code executes. Since this has to be done manually, clearly the trade-off here is the time to implement this robustness. I would strongly recommend using a code productivity tool such as ReSharper (e.g. going beyond Visual Studio snippets) to drop in these statements quickly rather than relying on typing or copy/paste.
Slide bullets:
- Provides ability to quickly locate bugs in your code
- Trade off is time/effort to implement
- Consider productivity tools such as ReSharper/CodeRush to lessen impact
What it looks like:
This is showing trace output with DebugView - notice I've configured DebugView to show me 'warning' trace statements in yellow and 'error' statements in red:
Tip #5 - design for flexibility
Again, this one is broken down into two tips:
Tip #5.1 - using SharePoint lists for configuration data
Explanation:
As I said in my presentation abstract, since the only certainties in life are death, taxes and clients changing their minds, we should always aim to develop a solution which is flexible enough to accommodate some of the changes we may be asked to implement. We're probably all used to the idea of storing site data which may change in SharePoint lists, but I like to extend this to 'configuration' information which dictates how the site behaves. The 'Config Store' framework I wrote for this can be found on Codeplex at www.codeplex.com/SPConfigStore - this provides the list, content type, caching and API to retrieve 'config items' in code. So to take an example from our project, we had a switch for whether newly-created users need to change their password on first logon. Clearly this is something we need enabled in production, but can be a pain in our test environments where the client needs to create users and then get on with running specific test scripts. So, by having such a switch in a SharePoint list, we get the benefit that key configuration of the site can be changed as easily as changing a SharePoint list item, as opposed to perhaps being stored in web.config where I'd need to RDP onto the server, open a file, trigger an app pool recycle etc.
We stored 130+ configuration settings in our Config Store list, and of course, applied appropriate permissions so that unauthorized users could not access the list.
Slide bullets:
- Use SP lists for values the client may wish to edit, but consider caching
What it looks like:
Tip #5.2 - implement a custom master page class
Explanation:
Although SharePoint gets master pages from .Net 2.0, these really deal with implementing a consistent look and feel only. If you want consistent functionality or behaviour across your pages, a custom master page class can help here. To implement this, the key is to modify the class which your master page derives from in the @Master directive. We used this approach to implement code which needs to execute on every page load, and even if you don't have this requirement from the start, I'd advocate using it so you have a convenient place to put such code if the need arises.
Slide bullets:
- Use for any code which should execute on every page load
- Examples on our site:
- Check if trial user/when trial access ends
- Check if accepted Terms & Conditions
- Check has supplied their initial user profile info
- Enforce use of HTTPS
- Can also use to expose properties for commonly checked items (e.g. username, logged in time)
What it looks like:
<%@ Master language="C#" Inherits="MyCompany.MyClient.MyProject.BasePage" %>
Conclusion/resources
So that's it for my tips, all feedback/suggestions welcome! In terms of key resources, in addition to AC's book here are some selected articles and so on I'd recommend looking into: