<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-4126985520350746834</atom:id><lastBuildDate>Fri, 10 Jul 2009 07:17:00 +0000</lastBuildDate><title>Chris O'Brien's blog</title><description>Fun and games with the nuts and bolts of SharePoint 2007.</description><link>http://www.sharepointnutsandbolts.com/</link><managingEditor>noreply@blogger.com (Chris O'Brien)</managingEditor><generator>Blogger</generator><openSearch:totalResults>83</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/ChrisObrien" type="application/rss+xml" /><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-5947294252644925769</guid><pubDate>Sun, 05 Jul 2009 21:08:00 +0000</pubDate><atom:updated>2009-07-05T22:08:56.825+01:00</atom:updated><title>Renewed as SharePoint MVP</title><description>&lt;p&gt;Like several others, I heard the good news from Microsoft last week that I’m a SharePoint MVP for another year. I’m really pleased to get the award again, as it’s nice to be recognized by Microsoft for what you’ve done. I find it useful (to me anyway!) to have a bit of reflection on what I’ve been up to for the last year. The big things were probably:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;A big re-write of my popular &lt;a href="http://spdeploymentwizard.codeplex.com/" target="_blank"&gt;SharePoint Content Deployment Wizard&lt;/a&gt; tool, to support added functionality such as scripted deployments&lt;/li&gt;    &lt;li&gt;Release of the &lt;a href="http://spconfigstore.codeplex.com" target="_blank"&gt;SharePoint Config Store&lt;/a&gt; framework, for storing configuration values in SharePoint development&lt;/li&gt;    &lt;li&gt;Release of the sister project, the &lt;a href="http://splanguagestore.codeplex.com/" target="_blank"&gt;SharePoint Language Store&lt;/a&gt; framework, for helping to build multi-lingual SharePoint sites&lt;/li&gt;    &lt;li&gt;Speaking at the &lt;a href="http://www.sharepointbestpractices.co.uk" target="_blank"&gt;European SharePoint Best Practices Conference&lt;/a&gt; on &lt;a href="http://www.sharepointnutsandbolts.com/2009/04/slide-deck-from-my-deployment-talk-at.html" target="_blank"&gt;deployment approaches/best practices for SharePoint sites&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;Being on various expert panels at different events (such as the Best Practices Conference)&lt;/li&gt;    &lt;li&gt;Other speaking gigs, like my &lt;a href="http://www.sharepointnutsandbolts.com/2008/10/my-top-5-wcm-tips-presentation.html" target="_blank"&gt;Lessons learnt/top 5 tips for building WCM sites in SharePoint&lt;/a&gt; session to a record-breaking audience at the UK SharePoint user group&lt;/li&gt;    &lt;li&gt;31 new articles and answers to over 100 questions/comments on this blog&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt; A busy year to be sure. I just want to say thanks to everybody that went to the trouble of passing on their thanks or feedback on some of this stuff – I &lt;em&gt;really&lt;/em&gt; appreciate it..&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-5947294252644925769?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/ZHuhVQEkBFM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/ZHuhVQEkBFM/renewed-as-sharepoint-mvp.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">7</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/07/renewed-as-sharepoint-mvp.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-8774934469687818165</guid><pubDate>Sun, 21 Jun 2009 22:47:00 +0000</pubDate><atom:updated>2009-06-22T08:09:42.785+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><category domain="http://www.blogger.com/atom/ns#">WCM</category><title>My checklist for optimizing SharePoint sites</title><description>&lt;p&gt;Optimization is probably one of my favorite SharePoint topics – it seems there’s always a new trick to learn, and I love the fact that a minor tweak can have a dramatic impact on how well your site works. There are some great resources out there now for information on how to boost performance, but it strikes me that there isn’t one single paper or article which contains all the aspects I personally consider or have been relevant to me on past projects. Hence this article is essentially an aggregation of some of these sources. The aim isn’t for it to be “complete” (if there is such a thing) or authoritative in any way, and I know I wouldn’t be the first person to blog on this subject – it really is just a reminder list for me to refer to, but if it helps you also that’s great.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Key resources:&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb727371.aspx" target="_blank"&gt;How to Optimize a SharePoint Server 2007 Web Content Management Site for Performance&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://technet.microsoft.com/en-us/library/cc263099.aspx" target="_blank"&gt;Optimizing Office SharePoint Server for WAN environments&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/cc879144.aspx" target="_blank"&gt;Prescriptive Guidance for SharePoint Server 2007 Web Content Management Sites&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://technet.microsoft.com/en-us/library/cc298550.aspx" target="_blank"&gt;Tune Web Server Performance (Office SharePoint Server)&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;Many blog articles/presentations by &lt;a href="http://www.andrewconnell.com/blog/" target="_blank"&gt;AC&lt;/a&gt;, &lt;a href="http://www.harbar.net" target="_blank"&gt;Spence&lt;/a&gt; and others&lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.stevesouders.com/blog/" target="_blank"&gt;Steve Souder’s (of Google) blog&lt;/a&gt;&amp;#160; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Before I break into the list, remember that different optimization measures will have different effects depending on your circumstances - having the best checklist in the world is really no substitute for the SharePoint architect’s ability to identify &lt;strong&gt;what&lt;/strong&gt; is likely to be the bottleneck for a given implementation. Additionally my list is quite “developer-focused”, but if the infrastructure aspects of the implementation is the limiting factor (e.g. latency between web servers and SQL, amount of usable RAM etc.) you can probably optimize code until you’re blue in the face without solving your performance problem. &lt;/p&gt;  &lt;p&gt;My list is broken down into sections, and some of the items have links to some useful resource or other which you might find helpful if the topic is new to you.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Configuration&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Items in this section typically have&amp;#160; a big impact – in particular output caching, which is the first thing to consider on WCM sites for example.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://technet.microsoft.com/en-us/library/cc261797.aspx" target="_blank"&gt;Output caching&lt;/a&gt; (or &lt;a href="http://msdn.microsoft.com/en-us/library/53a3xxk8.aspx" target="_blank"&gt;declarative caching at user control level&lt;/a&gt;) &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.andrewconnell.com/blog/archive/2008/11/15/Limiting-the-Page-Payload-with-IIS-HTTP-Compression.aspx" target="_blank"&gt;HTTP/IIS compression&lt;/a&gt;, including .aspx files &lt;/li&gt;    &lt;li&gt;&lt;a href="http://office.microsoft.com/en-us/sharepointserver/HA101762841033.aspx" target="_blank"&gt;BLOB caching&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/aa622758.aspx" target="_blank"&gt;Object caching&lt;/a&gt; configuration &lt;/li&gt;    &lt;li&gt;Is networking configured correctly e.g. &lt;a href="http://technet.microsoft.com/en-us/library/cc263061.aspx" target="_blank"&gt;cards set to gigabit&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;Is search indexing configured appropriately e.g. consider dedicated WFE for indexing, crawl frequency/type &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.sharepointnutsandbolts.com/2009/05/optimization-blob-caching-and-http-304s.html" target="_blank"&gt;Move CSS/JS files out of Style Library (to another library or to filesystem) due to HTTP 304s/BLOB cache bug&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;Check using &lt;a href="http://www.fiddlertool.com/" target="_blank"&gt;Fiddler&lt;/a&gt;/&lt;a href="http://developer.yahoo.com/yslow/" target="_blank"&gt;YSlow&lt;/a&gt; (more on this later) or similar for unexpected HTTP&amp;#160; 304, 401, 404 codes &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Code/development&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Test with &lt;a href="http://code.msdn.microsoft.com/SPDisposeCheck" target="_blank"&gt;SPDisposeCheck&lt;/a&gt; to ensure no memory leaks &lt;/li&gt;    &lt;li&gt;Measure page payload weight (again, &lt;a href="http://developer.yahoo.com/yslow/" target="_blank"&gt;YSlow&lt;/a&gt; useful here) – aim to reduce as far as possible &lt;/li&gt;    &lt;li&gt;Eliminate unnecessary ViewState! A good dev technique may be to turn off ViewState in web.config and then override it in controls/pages which really do need it (haven’t tried this, but keep meaning to). &lt;/li&gt;    &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb727371.aspx" target="_blank"&gt;Ensure not downloading core.js etc for anonymous users/delay-loading for authenticated&lt;/a&gt;. &lt;/li&gt;    &lt;li&gt;Ensure image sizes are small/&lt;a href="http://www.smushit.com" target="_blank"&gt;images are optimized for web&lt;/a&gt; (still comes up!) &lt;/li&gt;    &lt;li&gt;Always ensure height/width attributes specified on images &lt;/li&gt;    &lt;li&gt;Ensure &lt;a href="http://www.smashingmagazine.com/2008/08/18/7-principles-of-clean-and-optimized-css-code/" target="_blank"&gt;custom CSS is factored correctly&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;Don’t forget client-side code efficiency e.g. CSS, jQuery selectors &lt;/li&gt;    &lt;li&gt;Consider using a code-profiling tool such as &lt;a href="http://www.red-gate.com/products/ants_performance_profiler/index.htm" target="_blank"&gt;ANTS profiler&lt;/a&gt; or &lt;a href="http://www.jetbrains.com/profiler/features/" target="_blank"&gt;dotTrace&lt;/a&gt; to identify sub-optimal code &lt;/li&gt;    &lt;li&gt;Ensure general good coding practice e.g. your optimization work may well be undone if your code is badly-written. Accidentally doing unnecessary processing in a loop (in server-side OR client-side code is one example I’ve seen many times) &lt;/li&gt;    &lt;li&gt;Be wary of performance impact of custom HTTP modules – I frequently see huge perf gains when running load tests with such modules enabled/disabled. Removing core.js as a post-processing step within a HTTP module is NOT the right approach kids! (No really, I’ve seen it done..) &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Code/development – advanced&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;These tips might only really be necessary if you’re implementing a high-traffic site, but are worth considering:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Consider factoring custom JS/CSS into fewer files – fewer HTTP requests is better &lt;/li&gt;    &lt;li&gt;Consider &lt;a href="http://blog.mastykarz.nl/minify-your-javascript-and-save-the-kbs-for-later-imtech-javascript-minificator-free-tool/" target="_blank"&gt;“minifying” JS&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;Consider &lt;a href="http://paulstamatiou.com/how-to-optimize-your-site-with-image-sprites" target="_blank"&gt;using CSS to cluster background images&lt;/a&gt; (all images stitched into one for fewer HTTP requests) – &lt;a href="http://www.andrewconnell.com/blog" target="_blank"&gt;AC&lt;/a&gt; might have mentioned somewhere an online service to automate this.. &lt;/li&gt; &lt;/ul&gt;      &lt;p&gt;&lt;strong&gt;&lt;u&gt;Architecture&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;You should consider this section &lt;em&gt;absolutely &lt;/em&gt;incomplete – hopefully there’s enough here to dissuade anybody of the notion that it’s only about code though! The first two in particular are key.&lt;/p&gt;    &lt;ul&gt;     &lt;li&gt;&lt;a href="http://technet.microsoft.com/en-us/library/dd630764.aspx" target="_blank"&gt;64-bit&lt;/a&gt;/sufficient available RAM etc.    &lt;/li&gt;      &lt;li&gt;&lt;a href="http://technet.microsoft.com/en-us/library/cc263061.aspx" target="_blank"&gt;Latency between web and SQL – MS suggest a ping response of less than 1 millisecond&lt;/a&gt; &lt;/li&gt;      &lt;li&gt;Consider a CDN solution (e.g. &lt;a href="http://www.akamai.com/" target="_blank"&gt;Akamai&lt;/a&gt;, &lt;a href="http://www.limelightnetworks.com/" target="_blank"&gt;Limelight&lt;/a&gt; etc.) if users are geographically far away from servers (and y0ur client can afford it!) &lt;/li&gt;      &lt;li&gt;Ensure not using web garden with BLOB cache (known BLOB cache issue) &lt;/li&gt;      &lt;li&gt;Are &lt;a href="http://technet.microsoft.com/en-us/library/cc298550.aspx" target="_blank"&gt;app pool settings&lt;/a&gt; correct – check for excessive app pool recycling by enabling logging to the Windows event log using - &lt;strong&gt;cscript adsutil.vbs Set w3svc/AppPools/ &lt;em&gt;&amp;lt;YourAppPoolName&amp;gt;&lt;/em&gt; /LogEventOnRecycle 255&lt;/strong&gt;. Once or twice per day is probably the maximum you’re hoping for. &lt;/li&gt;      &lt;li&gt;Is storage correctly architected e.g. are OS, data, logs on different disk spindles? &lt;/li&gt;      &lt;li&gt;Is storage correctly configured (e.g. SAN configuration) – are your &lt;a href="http://en.wikipedia.org/wiki/Logical_Unit_Number" target="_blank"&gt;LUNS&lt;/a&gt; giving you sufficient &lt;a href="http://en.wikipedia.org/wiki/IOPS" target="_blank"&gt;IOPS&lt;/a&gt;?! &lt;/li&gt;   &lt;/ul&gt;    &lt;p&gt;&lt;strong&gt;&lt;u&gt;         &lt;br /&gt;Using YSlow to help optimize websites&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;     &lt;br /&gt;One particular tool worthy of discussion when we’re talking about optimizing websites is &lt;a href="http://developer.yahoo.com/yslow/" target="_blank"&gt;YSlow&lt;/a&gt; – if you haven’t come across it yet, this is a Firefox plugin which extends &lt;a href="http://getfirebug.com/" target="_blank"&gt;Firebug&lt;/a&gt;) developed by Yahoo! developers which uses their optimization ruleset (customizable) to report on and help further streamline your site. The tool focuses on communication between the server and browser – clearly a browser tool like this can’t identify any back-end issues, but it’s always interesting to run a site through the checks. &lt;/p&gt; &lt;/li&gt;  &lt;p&gt;The first tab gives a ‘grade’ for your site based on the ruleset, helping you identify areas for improvement (I’ve highlighted where the page stops and the YSlow console starts with a red box):&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_DPonmXhJQ4g/Sj6xLsaowjI/AAAAAAAAAbU/zSZDkIgzbmA/s1600-h/YSlow_Grading%5B5%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="YSlow_Grading" border="0" alt="YSlow_Grading" src="http://lh3.ggpht.com/_DPonmXhJQ4g/Sj6xNgV2bwI/AAAAAAAAAbY/2IGfw2r8S0k/YSlow_Grading_thumb%5B3%5D.png?imgmax=800" width="826" height="640" /&gt;&lt;/a&gt;&amp;#160; &lt;br /&gt; The next tab provides a breakdown of files downloaded by the request, and allows me to see their size and headers etc. In particular I can see the expiry date and Etag and so on which the files are being tagged with so they can be cached locally:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_DPonmXhJQ4g/Sj6xO14qI3I/AAAAAAAAAbc/YA9yb1rnyBo/s1600-h/YSlow_Components%5B5%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="YSlow_Components" border="0" alt="YSlow_Components" src="http://lh4.ggpht.com/_DPonmXhJQ4g/Sj6xQ5RaB8I/AAAAAAAAAbg/7e7evp51tKE/YSlow_Components_thumb%5B3%5D.png?imgmax=800" width="826" height="491" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;The statistics tab provides some good analysis on the page weight and also illustrates the difference between a first visit and subsequent visits where appropriately tagged files will be served from the browser cache:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_DPonmXhJQ4g/Sj6xSCPVAHI/AAAAAAAAAbk/YnhlCilHjxY/s1600-h/YSLow_Stats%5B5%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="YSLow_Stats" border="0" alt="YSLow_Stats" src="http://lh6.ggpht.com/_DPonmXhJQ4g/Sj6xTteFCtI/AAAAAAAAAbo/IR6GXVP9XK4/YSLow_Stats_thumb%5B3%5D.png?imgmax=800" width="826" height="577" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Finally the Net tab in &lt;a href="http://getfirebug.com/" target="_blank"&gt;Firebug&lt;/a&gt; is also interesting, as this shows me how files were downloaded (sequential or in parallel) – the most recent browsers do a much better job of this, with IE8 and FF3 being able to open 6 channels per URL domain to download files in parallel, but note that IE7 could only open 2. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_DPonmXhJQ4g/Sj6xVe2q0oI/AAAAAAAAAbs/Csi4GNRwgPM/s1600-h/Firebug_NetAnalysis%5B5%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Firebug_NetAnalysis" border="0" alt="Firebug_NetAnalysis" src="http://lh5.ggpht.com/_DPonmXhJQ4g/Sj6xXRnhWgI/AAAAAAAAAbw/2ubtnaEaa5U/Firebug_NetAnalysis_thumb%5B3%5D.png?imgmax=800" width="826" height="567" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;From this, I also see the &lt;a href="http://sharepoint.microsoft.com"&gt;http://sharepoint.microsoft.com&lt;/a&gt; site (great site btw) I’ve been analyzing also displays the &lt;a href="http://www.sharepointnutsandbolts.com/2009/05/optimization-blob-caching-and-http-304s.html" target="_blank"&gt;BLOB cache issue I discussed recently&lt;/a&gt; (now confirmed as a bug) where incorrect headers are added to files stored in the Style Library, causing lots of unnecessary HTTP 304s to go across the wire. So YSlow does give a great insight – however I do agree with &lt;a href="http://www.codinghorror.com/blog/archives/000932.html" target="_blank"&gt;Jeff Atwood’s reminder&lt;/a&gt; that some of the reported “issues” may not be the biggest concern for your site. As always, apply common sense.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Summary&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Optimization is a deep topic, and this checklist is simply my reminder of some of the things to consider, though in reality a solid understanding of the nuts and bolts is required to really architect and develop high-performing sites. Tools like YSlow can also help with some aspects of optimization. &lt;/p&gt;  &lt;p&gt;So which optimization nuggets did I miss which you consider? Feel free to leave a comment and add to this list..&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-8774934469687818165?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/iyRVP9aNNlo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/iyRVP9aNNlo/my-checklist-for-optimizing-sharepoint.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/06/my-checklist-for-optimizing-sharepoint.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-5934570945027610875</guid><pubDate>Sun, 31 May 2009 21:06:00 +0000</pubDate><atom:updated>2009-05-31T22:07:10.605+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><title>More on optimization, HTTP 304s etc. – a solution?</title><description>&lt;p&gt;In my last post &lt;a href="http://www.sharepointnutsandbolts.com/2009/05/optimization-blob-caching-and-http-304s.html"&gt;Optimization, BLOB caching and HTTP 304s&lt;/a&gt;, I did a fairly lengthy walk-through on an issue I’d experienced with SharePoint publishing sites. A few people commented, mainly saying they’d noticed the same thing, but there have been further developments and findings I wanted to share!&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Quick recap&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Under certain circumstances some files in SharePoint are always re-requested by the browser despite being present in the browser cache (“Temporary internet files”). Specifically this is observed for&lt;strong&gt; files stored in the Style Library and Master Page Gallery, for anonymous users&lt;/strong&gt;. Although SharePoint responds with a HTTP 304 to say the cached file can indeed be used (as opposed to sending the &lt;em&gt;file itself&lt;/em&gt; again), we effectively have an unnecessary round-trip to the server for each file – and there could be many such files when all the page’s images/CSS/JS files are considered. This extra network traffic can have a tangible impact on site performance, and this is magnified if the user is geographically far away from the server.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;A solution?&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blog.mastykarz.nl/" target="_blank"&gt;Waldek&lt;/a&gt; and I have been tossing a few development matters around recently over e-mail, and he was curious enough to investigate this issue for himself. After reproducing it and playing around for some time, Waldek discovered that flushing the disk-based cache seems to cause a change in behaviour – or in layman’s terms, fixes everything. To be more specific, we’re &lt;em&gt;assuming&lt;/em&gt; it’s a flush of the BLOB cache which is having the affect – in both Waldek’s test and my subsequent validation, the object cache was also flushed as well:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_DPonmXhJQ4g/SiBapniidnI/AAAAAAAAAaY/hPMC3Dy247A/s1600-h/FlushDiskCache5.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="FlushDiskCache" border="0" alt="FlushDiskCache" src="http://lh5.ggpht.com/_DPonmXhJQ4g/SiBaq4IZpmI/AAAAAAAAAac/i6mwg52YUfQ/FlushDiskCache_thumb3.jpg?imgmax=800" width="1008" height="471" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;After the OK button is hit on this page, the problem seems to go away completely, so &lt;em&gt;now &lt;/em&gt;when the page is accessed now for the first time as an anonymous user, the correct ‘max-age’ header is added to the files (as per the BLOB cache declaration in web.config) – contrast the ‘max-age=86400’ header on the Style Library files with what I documented in my &lt;a href="http://www.sharepointnutsandbolts.com/2009/05/optimization-blob-caching-and-http-304s.html" target="_blank"&gt;last post&lt;/a&gt;: &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_DPonmXhJQ4g/SiBary4TG-I/AAAAAAAAAag/Z-fXzNTVBFQ/s1600-h/AnonymousCorrectHeadersAfterFlushCac%5B2%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="AnonymousCorrectHeadersAfterFlushCache" border="0" alt="AnonymousCorrectHeadersAfterFlushCache" src="http://lh3.ggpht.com/_DPonmXhJQ4g/SiBatoSsGrI/AAAAAAAAAak/-Az6QCqqCw0/AnonymousCorrectHeadersAfterFlushCac%5B1%5D.jpg?imgmax=800" width="809" height="680" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;This means that on subsequent requests, the Style Library files are served directly from the browser cache with no 304 round-trip:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_DPonmXhJQ4g/SiBauLFdJAI/AAAAAAAAAao/0W-DUA48VvU/s1600-h/SecondRequestNo304s%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="SecondRequestNo304s" border="0" alt="SecondRequestNo304s" src="http://lh6.ggpht.com/_DPonmXhJQ4g/SiBaulnTOgI/AAAAAAAAAas/_mpRBmH1NGg/SecondRequestNo304s_thumb%5B2%5D.jpg?imgmax=800" width="814" height="145" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;This is great news, as it means the issue I described is essentially a non-issue, and there is therefore no performance penalty for storing files in the publishing Style Library.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;So what gives?&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;I’m now wondering if this is just a ‘gotcha’ with BLOB caching and publishing sites. I know other people have run into the original issue due to the &lt;a href="http://www.sharepointnutsandbolts.com/2009/05/optimization-blob-caching-and-http-304s.html#comments" target="_blank"&gt;comments on my previous post&lt;/a&gt;, and interestingly enough one poster said they use reverse proxy techniques specifically to deal with this issue. Could it really be that everybody who sees this behaviour just didn’t flush the BLOB cache somewhere along the way, when it’s actually a required step? Or is the testing that Waldek and I did flawed in some way? Or indeed, was my initial investigation flawed despite the fact others reported the same issue?&lt;/p&gt;  &lt;p&gt;I am interested to hear from you on this – if you can reproduce the problem I’ve described with a publishing site you’ve developed, does flushing the BLOB cache solve it for you as described here? Leave a comment and let us know!&lt;/p&gt;  &lt;p&gt;Good work Waldek :-)&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-5934570945027610875?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/GWM_cBJGIog" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/GWM_cBJGIog/more-on-optimization-http-304s-etc.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">11</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/05/more-on-optimization-http-304s-etc.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-4545026441652014887</guid><pubDate>Sun, 17 May 2009 00:21:00 +0000</pubDate><atom:updated>2009-05-17T10:54:10.089+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><title>Optimization, BLOB caching and HTTP 304s</title><description>&lt;p&gt;There's been an interesting mini-debate going on recently in terms of where to store static assets used by your site - images, CSS, JS files and so on. Broadly the two approaches can be characterized as:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Developer-centric - store assets on the filesystem, perhaps in the 12 hive &lt;/li&gt;   &lt;li&gt;Author-centric - store assets in the content database, perhaps in the Style Library which comes with publishing sites &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Needless to say these options offer different pros and cons depending on your requirements - &lt;a href="http://hermansberghem.blogspot.com"&gt;Serv&amp;#233; Hermans&lt;/a&gt; offers a good analysis in &lt;a href="http://hermansberghem.blogspot.com/2009/04/to-package-or-not-to-package-that-is.html"&gt;To package or not to package: that is the question&lt;/a&gt;. However, I want to throw another point into the debate - performance, specifically for anonymous users. Frequently, this is an audience I care deeply about since some of the WCM sites I work on often have forecast ratios of 80% anonymous vs. 20% authenticated users. Recently I was asked to help optimize an under-performing airline site built on MOSS - as usual the problem was a combination of several things, but one of the high-impact items was this decision to store assets in one location over the other. In this post I'll explain what the effect on performance is and why you should consider this when building your site.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;The problem &lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Once they've been loaded the first time, most of the static files a website uses should be served from the user's local browser cache (&amp;quot;Temporary internet files&amp;quot;) - without this, the internet would be seriously slow. Consider how much slower a web page loads when you do a hard refresh (ctrl+F5) compared to normal - this is because all the images are forced to be re-downloaded rather than served from the browser cache. Unfortunately, &lt;strong&gt;for files stored in some common SharePoint libraries/galleries (i.e. the author-centric approach)&lt;/strong&gt; SharePoint doesn't deal with this quite&lt;em&gt; &lt;/em&gt;right in some scenarios - &lt;em&gt;most &lt;/em&gt;of the gain is there, but despite having the image locally, the browser still makes a request for the image - the conversation goes like this (for EACH image on the page!):&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Browser: &lt;/strong&gt;I need this image please - I cached it last time I came at [date/time], but for all I know it's changed since then.     &lt;br /&gt;&lt;strong&gt;Server: &lt;/strong&gt;No need dude, it's not changed so just use your local copy (in the form of a HTTP 304 - &amp;quot;Not modified&amp;quot;)     &lt;br /&gt;&lt;strong&gt;Browser: &lt;/strong&gt;Fair enough, cheers.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;This essentially happens because the file was not served with a &amp;quot;cacheability&amp;quot; HTTP header to begin with. &lt;/em&gt;Needless to say, this adds significant time to the page load when you have 30+ images/CSS/JS files referenced on your page - potentially several seconds in my experience (under some circumstances), which of course is a huge deal. If say, the user is in Europe but the servers are in the U.S., then suddenly this kind of network chatter is something we need to address. Needless to say, in the majority of cases we're happy to cache these files for a period since they don't all change too often, and we get better performance as a result.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;The Solution (for &lt;em&gt;some&lt;/em&gt; SharePoint libraries *)&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Mike Hodnick points us to part of the solution in his highly-recommended article &lt;a href="http://www.kindohm.com/archive/2008/06/20/eliminating-quot304quot-status-codes-with-sharepoint-web-folder-resources.aspx" target="_blank"&gt;Eliminating &amp;quot;304&amp;quot; status codes with SharePoint web folder resources&lt;/a&gt;. Essentially, &lt;a href="http://office.microsoft.com/en-gb/sharepointserver/HA101762841033.aspx"&gt;SharePoint's BLOB caching feature&lt;/a&gt; saves the day since it serves the image with a &amp;quot;max-age&amp;quot; value on the HTTP header, meaning the browser knows it can use it's local copy of the file until this date. This only happens when BLOB caching is enabled &lt;em&gt;and has the max-age attribute &lt;/em&gt;like this (here set to 84600 seconds = 24 hours):&lt;/p&gt;  &lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;   &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;BlobCache&lt;/span&gt; &lt;span style="color: #ff0000"&gt;location&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;C:\blobCache&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;path&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;\.(gif|jpg|png|css|js|aspx)$&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;maxSize&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;10&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;enabled&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;true&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;max-age&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;86400&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;When we configure the BLOB cache like this we are, in effect, specifying that it's OK to cache static files for a certain period, so the &amp;quot;cacheable&amp;quot; header gets added. &lt;strong&gt;HOWEVER, what Mike doesn't cover is that this only happens for authenticated users&lt;/strong&gt; - files served out of common content DB locations such as the Style Library and Master Page Gallery still do not get served correctly to anonymous users. Note this isn't all SharePoint libraries though - so we need to be clear on exactly when this problem occurs.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;* Scope of this problem/solution&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Before drilling down any deeper, let's stop for a moment and consider the scope of what we're discussing - a site with:&lt;/p&gt;&lt;ul&gt; &lt;li&gt;Anonymous users &lt;/li&gt;&lt;li&gt;Files stored in &lt;em&gt;some &lt;/em&gt;libraries - I'm not 100% sure of the pattern but discuss it later - the Style Library and Master Page Gallery are known culprits however. Other OOTB libraries such as SiteCollectionImages do &lt;em&gt;not&lt;/em&gt; have the problem. &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you don't have this combination of circumstances, you likely don't have the problem. For those who do, we're now going to look closer at what's going on, before concluding with how we can work around the issue at the end.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Drilling deeper&lt;/u&gt;&lt;/strong&gt; &lt;/p&gt;&lt;p&gt;For a site which &lt;em&gt;does&lt;/em&gt; have the above combination of circumstances, we can see the issue with Fiddler - as an &lt;strong&gt;anonymous&lt;/strong&gt; user browsing to page I've already visited, I see a stack of 304s meaning the browser is re-requesting all these files:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_DPonmXhJQ4g/Sg8DGIbctCI/AAAAAAAAAZw/O0whAVBK22c/s1600-h/BlobCachingDisabled_304s%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" alt="BlobCachingDisabled_304s" src="http://lh5.ggpht.com/_DPonmXhJQ4g/Sg8DICKl5VI/AAAAAAAAAZ0/ewRkmLlMB_o/BlobCachingDisabled_304s_thumb%5B2%5D.jpg?imgmax=800" width="853" height="573" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;However, if I'm &lt;strong&gt;authenticated&lt;/strong&gt; and I navigate to the same page, I only see the HTTP 200 for the actual page, no 304s:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_DPonmXhJQ4g/Sg8DIrMqcJI/AAAAAAAAAZ4/wK2tyTX_G64/s1600-h/BlobCachingEnabled_No304s%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" alt="BlobCachingEnabled_No304s" src="http://lh6.ggpht.com/_DPonmXhJQ4g/Sg8DJaYpJOI/AAAAAAAAAZ8/DBxJqqyxLyE/BlobCachingEnabled_No304s_thumb%5B2%5D.jpg?imgmax=800" width="879" height="291" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Hence we can conclude it works fine for authenticated users but not for anonymous users.&lt;/p&gt;&lt;p&gt;So what can we do for our poor anonymous users (who might be in the majority) if we're storing files in the problematic libraries? Well, here's where I draw a blank unfortunately. &lt;a href="http://technet.microsoft.com/en-us/library/cc263099.aspx" target="_blank"&gt;Optimizing Office SharePoint Server for WAN environments&lt;/a&gt; on TechNet has this to say on the matter: &lt;/p&gt;&lt;p&gt;&lt;em&gt;Some lists don't work by default for anonymous users. If there are anonymous users accessing the site, permissions need to be manually configured for the following lists in order to have items within them cached: &lt;/p&gt;&lt;ul&gt;&lt;li&gt;Master Page Gallery&lt;/li&gt;&lt;li&gt;Style Library&lt;/li&gt;&lt;/ul&gt;&lt;/em&gt;&lt;p&gt;Aha! So we need to change some permissions - fine. This seems to indicate that it &lt;em&gt;is, &lt;/em&gt;in fact, possible to get the correct cache headers added to files served from these locations. Unfortunately, I simply &lt;strong&gt;&lt;em&gt;cannot&lt;/em&gt;&lt;/strong&gt; find what permissions need to be changed, and nobody on the internet (including the TechNet article) seems to detail what. The only logical setting is the Anonymous Access options for the list - these are all clear by default, but adding the 'View Items' permission (as shown below) does not change anything:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_DPonmXhJQ4g/Sg9bFUMB87I/AAAAAAAAAaI/8H9Uyw1SjLs/s1600-h/AnonPermissions%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" alt="AnonPermissions" src="http://lh6.ggpht.com/_DPonmXhJQ4g/Sg9bGHoW-PI/AAAAAAAAAaM/QgkJmb8irt4/AnonPermissions_thumb%5B2%5D.jpg?imgmax=800" width="618" height="301" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;As a sidenote, the setting above is (I believe) effectively granting read permissions to the identity which is used for anonymous access to the associated IIS site. So in IIS 7.0, I'm fairly sure you'd achieve the same thing by doing this:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_DPonmXhJQ4g/Sg9bG9JbiAI/AAAAAAAAAaQ/Fh3s26HTk10/s1600-h/AddPermsIUsr%5B14%5D.jpg"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" alt="AddPermsIUsr" src="http://lh5.ggpht.com/_DPonmXhJQ4g/Sg9bPAOVCuI/AAAAAAAAAaU/7IlCPAWvPlY/AddPermsIUsr_thumb%5B8%5D.jpg?imgmax=800" width="958" height="577" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;So the problem does not go away when anonymous users are granted the 'View Items permission, and what I find interesting about this is that a closer look with Fiddler reveals some inconsistencies. The image below shows me browsing to a page anonymously for the first time, and to save you the hassle we can derive the following findings:&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;Files served from the 'SiteCollectionImages' library are given the correct max-age header (perhaps expected, since not one of the known 'problem libraries' e.g. Style Library) &lt;/li&gt;  &lt;li&gt;Files served from the '_layouts' folder are given a different max-age header (expected, settings from the IIS site are used here) &lt;/li&gt;  &lt;li&gt;&lt;strong&gt;&lt;em&gt;Some&lt;/em&gt; files in the Style Library are in fact given a the correct max-age header! (not expected)&lt;/strong&gt;&amp;#160; &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_DPonmXhJQ4g/Sg8DKYs0HsI/AAAAAAAAAaA/6vayMRKkx-c/s1600-h/MixedHeaders_Anonymous%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" alt="MixedHeaders_Anonymous" src="http://lh3.ggpht.com/_DPonmXhJQ4g/Sg8DNR6W3QI/AAAAAAAAAaE/IrZhnYwhDYo/MixedHeaders_Anonymous_thumb%5B2%5D.jpg?imgmax=800" width="865" height="417" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;So the 2 questions which strike me here are:&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;Why are some files being served from 'Style Library' with the correct header when most aren't? &lt;/li&gt;  &lt;li&gt;Why can SharePoint add the 'max-age' header to files in the 'SiteCollectionImages' library but not the 'Style Library'? &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The first one is a mystery to me - it's perhaps not too important, but I can't work it out. The second one &lt;em&gt;might&lt;/em&gt; be down to how the libraries are provisioned - the 'Style Library' is provisioned by declarative XML in the 'PublishingResources' Feature, whereas the 'SiteCollectionImages' library is provisioned in code using the same Feature's activation receiver. Could this be the key factor? I don't know, but I'd certainly be interested if anyone can put me straight - either on this or the mystery &amp;quot;permissions change&amp;quot; required to make BLOB caching deal with libraries such as the 'Style Library'. &lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Conclusion&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;The key takeaway here is that for sites which want to take advantage of the browser caching for static files (for performance reasons) and have anonymous users, we need to be careful where we put our images/CSS/JS files as per Mike Hodnick's general message. If we want to use the author-centric approach and store things in SharePoint libraries, we need to consider &lt;em&gt;which &lt;/em&gt;libraries (and test) if we will have the 304 problem. Alternatively, we can choose to store these files on the filesystem (the developer-centric approach) and use a virtual directory with the appropriate cacheability settings to suit our needs. My suggestion would be to use a custom virtual directory for full control of this, since the default settings on the '_layouts' directory (&amp;quot;cache for 1 year&amp;quot;) are unlikely to be appropriate.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-4545026441652014887?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/vxJ6J_zLZZI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/vxJ6J_zLZZI/optimization-blob-caching-and-http-304s.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">9</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/05/optimization-blob-caching-and-http-304s.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-1840873725700027620</guid><pubDate>Mon, 04 May 2009 22:29:00 +0000</pubDate><atom:updated>2009-05-04T23:34:02.148+01:00</atom:updated><title>Fix to my Language Store framework for multi-lingual sites</title><description>&lt;p&gt;In my last post, I talked about a &lt;a href="http://www.sharepointnutsandbolts.com/2009/04/fix-to-my-config-store-framework-and.html" target="_blank"&gt;fix to my Config Store framework&lt;/a&gt; for an issue which manifested itself on certain SharePoint builds, with Windows 2008 and a recent cumulative update seeming to be the trigger. Some of you may know that I produced a sister project to this one called the '&lt;a href="http://splanguagestore.codeplex.com" target="_blank"&gt;Language Store&lt;/a&gt;', which is designed to help build multi-lingual SharePoint sites - since this framework is built off the same underlying XML and plumbing, this solution was also affected.&amp;#160; So this post is just a short one to say that the fix has now been applied to the Language Store framework, and the new version is now available on Codeplex at &lt;a title="http://splanguagestore.codeplex.com" href="http://splanguagestore.codeplex.com"&gt;http://splanguagestore.codeplex.com&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Problem/solution&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;The problem was effectively that items in the SharePoint list could no longer be edited - well, in fact they could be updated &lt;em&gt;using code&lt;/em&gt;, but the list form .aspx pages were not showing the fields correctly so items couldn't be edited in the UI. Since it kind of defeats the point of SharePoint to have to write code to update list items (!), this was a big issue on affected builds. Interestingly some users reported working around the issue by removing/re-adding the content type from the list in the browser, but happily this is no longer necessary since the root issue has now been resolved. The problem was traced to some incorrect XML in my FieldRef elements - see the last post &lt;a href="http://www.sharepointnutsandbolts.com/2009/04/fix-to-my-config-store-framework-and.html" target="_blank"&gt;Fix to my Config Store framework and list provisioning tips&lt;/a&gt; for the full info.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;General recap - the Language Store&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;If you're still reading, I figure some folks would welcome a reminder/intro on what the Language Store actually does - it's &lt;em&gt;not&lt;/em&gt; about replacing SharePoint's variations functionality which is commonly used on multi-lingual sites. I noticed &lt;a href="http://www.harbar.net" target="_blank"&gt;Spence&lt;/a&gt; gave it a better name in an e-mail recently where he described it as a '&lt;em&gt;term store&lt;/em&gt;' for multi-lingual sites - this actually captures what it does far better than my name for it. Effectively the idea is to provide a framework for the many small strings of text &lt;em&gt;which are not part of authored page content &lt;/em&gt;which need to be translated and displayed in the appropriate language. As an example, here is a page from the BBC site where I've highlighted all the strings which may need to be translated but which don't belong to a particular page:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_DPonmXhJQ4g/Sf9sXjOXEGI/AAAAAAAAAZo/clTgwSgrNQM/s1600-h/BBCExample%5B4%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="BBCExample" src="http://lh3.ggpht.com/_DPonmXhJQ4g/Sf9sYsUt6gI/AAAAAAAAAZs/vMlYPtmOMQk/BBCExample_thumb%5B2%5D.jpg?imgmax=800" width="640" height="392" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;There are many of these in a typical multi-lingual site, and to help deal with this requirement the Language Store framework provides the following:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;SharePoint list/content type/site columns etc.&lt;/li&gt;    &lt;li&gt;API to retrieve items with a single line of code&lt;/li&gt;    &lt;li&gt;Granular caching for high-performance&lt;/li&gt;    &lt;li&gt;Packaged as a .wsp for simple deployment&lt;/li&gt;    &lt;li&gt;All source code/XML freely available&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;If you want to find out more, see &lt;a href="http://www.sharepointnutsandbolts.com/2008/12/building-multi-lingual-sharepoint-sites.html" target="_blank"&gt;Building multi-lingual SharePoint sites - introducing the Language Store&lt;/a&gt;. The solution can be downloaded from the Codeplex site at &lt;a title="http://splanguagestore.codeplex.com" href="http://splanguagestore.codeplex.com"&gt;http://splanguagestore.codeplex.com&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Apologies to existing users who were affected by the issue. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-1840873725700027620?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/WKz_QT0bO30" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/WKz_QT0bO30/fix-to-my-language-store-framework-for.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/05/fix-to-my-language-store-framework-for.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-6614780955433100995</guid><pubDate>Wed, 22 Apr 2009 23:42:00 +0000</pubDate><atom:updated>2009-04-23T00:46:38.121+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">features</category><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><title>Fix to my Config Store framework and list provisioning tips</title><description>&lt;p&gt;Had a couple of reports recently of an issue with my &lt;a href="http://www.codeplex.com/SPConfigStore" target="_blank"&gt;Config Store&lt;/a&gt; solution, which provides a framework for using a SharePoint list to store configuration values. If you're using the Config Store this article will definitely be of interest to you, but I've also picked up a couple of general tips on list provisioning which I want to pass on. I have to thank Richard Browne (no blog) of my old company &lt;a href="http://www.cscape.com" target="_blank"&gt;cScape&lt;/a&gt;, as the fix and several of the tips have come from him - as well as alerting me to the problem, he also managed to fix it before I did, so many thanks and much kudos mate :-)&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Config Store problem&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Under some circumstances, fields in the Config Store list were not editable because they no longer appeared on the list edit form (EditForm.aspx). So instead of having 4 editable fields, only the 'Config name' field shows in the form:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_DPonmXhJQ4g/Se-hbynSB2I/AAAAAAAAAZg/I7JNaUEM1Bw/s1600-h/ConfigStoreMissingFields%5B5%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="ConfigStoreMissingFields" src="http://lh6.ggpht.com/_DPonmXhJQ4g/Se-hcjp0ChI/AAAAAAAAAZk/BdJDMfgU2iI/ConfigStoreMissingFields_thumb%5B3%5D.png?imgmax=800" width="640" height="272" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;I've not fully worked out the pattern, but I &lt;em&gt;think&lt;/em&gt; the problem may only appear if you provision the list on a server which has the October or December Cumulative Update installed - either that or it's a difference between Windows 2003 and Windows 2008 environments (which would be even more bizarre). Either way, it seems something changed in the way the provisioning XML was handled somewhere. This is why the problem was undetected in the earlier releases. &lt;/p&gt;  &lt;p&gt;I &lt;em&gt;had &lt;/em&gt;seen this problem before - but only when the list was moved using Content Deployment (e.g. using the &lt;a href="http://www.codeplex.com/SPDeploymentWizard" target="_blank"&gt;Content Deployment Wizard&lt;/a&gt;) - the original 'source' list was always fine. We managed to work around this by writing some code which 're-added' the fields to the list from the content type, since they were always actually present on the content type and the data was still corrected stored. Having to run this code every time we deployed the list was an irritation rather than critical, but something I wanted to get to the bottom of - however, on finding some folks were running into this in 'normal' use meant that it became a bigger issue.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;The cause&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;I always knew the problem would be down to a mistake in the provisioning XML, but since I'd looked for it on previous occasions I knew it was something I was seeing but not seeing. In my case, Richard spotted that I was using the wrong value in my FieldRef elements under the ContentType element - I was mistakenly thinking that the 'Name' attribute needed to match up with the ''StaticName' attribute given to the field; the documentation says this attribute contains the internal name of the field. So my FieldRefs looked like this:&lt;/p&gt;  &lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;   &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;ContentType&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ID&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;0x0100E3438B2389F84cc3965600BC16BF32E7&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Config item&amp;quot;&lt;/span&gt; &lt;br /&gt;               &lt;span style="color: #ff0000"&gt;Group&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Config Store content types&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Description&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Represents an item in the config store.&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Version&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;FieldRefs&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;FieldRef&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ID&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{33F5C8B4-A6BB-41a4-AB24-69F2152974C5}&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;ConfigCategory&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Required&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;TRUE&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;FieldRef&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ID&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{BD413479-48AB-41f5-8040-918F32EBBCC5}&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;ConfigValue&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Required&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;TRUE&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;FieldRef&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ID&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{84D42C64-D0BD-4c76-8ED3-0A9E0D261111}&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;ConfigItemDescription&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;FieldRefs&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;ContentType&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;..to match up with fields which looked like this:&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;  &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;p&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;Field&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ID&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{33F5C8B4-A6BB-41a4-AB24-69F2152974C5}&amp;quot;&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #ff0000"&gt;Name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Config category&amp;quot;&lt;br /&gt;      &lt;span style="color: #ff0000"&gt;DisplayName&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Config category&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: #ff0000"&gt;StaticName&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;ConfigCategory&amp;quot;&lt;/span&gt;&lt;br /&gt;      ....&lt;br /&gt;      ....&lt;br /&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;  &lt;/p&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The CORRECTED version looks like this (note the change in value for the Name attribute of FieldRefs):&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;br /&gt;  &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;ContentType&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ID&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;0x0100E3438B2389F84cc3965600BC16BF32E7&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Config item&amp;quot;&lt;/span&gt;&lt;br /&gt;             &lt;span style="color: #ff0000"&gt;Group&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Config Store content types&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Description&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Represents an item in the config store.&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Version&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;FieldRefs&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;FieldRef&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ID&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{33F5C8B4-A6BB-41a4-AB24-69F2152974C5}&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Config category&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Required&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;TRUE&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;FieldRef&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ID&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{BD413479-48AB-41f5-8040-918F32EBBCC5}&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Config value&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Required&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;TRUE&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;FieldRef&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ID&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;{84D42C64-D0BD-4c76-8ED3-0A9E0D261111}&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Name&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Config item description&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;FieldRefs&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;ContentType&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So, the main learning I got from this is to remember that &lt;strong&gt;the 'Name' of the FieldRef attribute needs to match the 'Name' of the Field attribute &lt;/strong&gt;- that simple. Why did it work before? No idea unfortunately.&lt;/p&gt;&lt;p&gt;However, I also picked up a few more things I didn't know about, partly from Richard (this guy needs a blog!) and partly from some other reading/experimenting.. &lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Some handy things to know about list provisioning&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;To make a field mandatory on a list, the 'Required' attribute must be 'TRUE'. Not 'True' or 'true' - this is one of the cases where the provisioning framework is pernickety about that 6-choice boolean ;-)&lt;/li&gt;  &lt;li&gt;FieldRefs need an ID and Name as a minimum (which must match the values in the 'Field' declaration), but you can override certain other things here like the DisplayName - this mirrors what is possible in the UI.&lt;/li&gt;  &lt;li&gt;You don't have to include the list .aspx files (DispForm.aspx, EditForm.aspx and NewForm.aspx) in your Feature if you use the 'SetupPath' attribute in the 'Form' element in schema.xml (assuming you don't need to associate custom list forms).&lt;/li&gt;  &lt;li&gt;You can use the 'ContentTypeRef' element to associate your content type with the list (specify just content type ID), rather than using the 'ContentType' element which needs to redeclare all the FieldRefs.&lt;/li&gt;  &lt;li&gt;It's safe to remove all the default 'system' fields from the 'Fields' section of schema.xml&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Going further than these tips, the best thing I found on this is Oskar Austegard's &lt;a href="http://mo.notono.us/2009/02/moss-dreaded-schemaxml.html" target="_blank"&gt;MOSS: The dreaded schema.xml&lt;/a&gt; which shows how you can strip a &lt;em&gt;ton&lt;/em&gt; of stuff out of schema.xml. I've not tried it yet, but I'm sure that will be my starting point for the next list I provision declaratively. If you're interested in the nuts and bolts of list provisioning, I highly recommend you read it. &lt;/p&gt;&lt;p&gt;Happy XML'ing..&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-6614780955433100995?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/lzPq5yNrTRs" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/lzPq5yNrTRs/fix-to-my-config-store-framework-and.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">7</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/04/fix-to-my-config-store-framework-and.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-4408907796071328100</guid><pubDate>Tue, 14 Apr 2009 22:20:00 +0000</pubDate><atom:updated>2009-04-19T17:59:48.800+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><category domain="http://www.blogger.com/atom/ns#">speaking</category><title>Slide deck from my deployment talk at Best Practices Conference</title><description>&lt;p&gt;Had a great time presenting at the European SharePoint Best Practices Conference last week. I've been trying to put my finger on what made it such a good conference and I'm actually not sure, but I notice that other speakers and attendees have also been full of praise, so it's not just me. The event itself was extremely well-organized with excellent content, and Steve Smith and his team did a great job of looking after us speakers.&lt;/p&gt;  &lt;p&gt;Highlights for me on the dev track were sessions from &lt;a href="http://www.andrewconnell.com/blog" target="_blank"&gt;AC&lt;/a&gt;, &lt;a href="http://sharepoint.mindsharpblogs.com/todd" target="_blank"&gt;Todd Bleeker&lt;/a&gt;, &lt;a href="http://www.binarywave.com/blogs/eshupps/default.aspx" target="_blank"&gt;Eric&lt;/a&gt; (or &amp;quot;Uncle Eric&amp;quot; as I like to think of him, with his wise words on high-performance coding :-)) and &lt;a href="http://www.21apps.com" target="_blank"&gt;Andrew Woody&lt;/a&gt;, but whenever I did stray from developer content I seemed to run into a great session like &lt;a href="http://www.sharepointmadscientist.com/" target="_blank"&gt;Mike Watson&lt;/a&gt;'s on SQL Server in relation to SharePoint. Similarly I heard good things about speakers like &lt;a href="http://community.zevenseas.com/Blogs/Daniel" target="_blank"&gt;Dan McPherson&lt;/a&gt; doing innovative sessions on the Information Worker track which I was disappointed to miss. [UPDATE: Here's a gratuitous shot of me in my session:]&lt;/p&gt;  &lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="COB_BestPracticesTalk_2" src="http://lh4.ggpht.com/_DPonmXhJQ4g/SetYgNusX3I/AAAAAAAAAZY/97RF4mOgxCw/COB_BestPracticesTalk_2%5B6%5D.jpg?imgmax=800" width="608" height="457" /&gt;&lt;/p&gt;  &lt;p&gt;Another highlight was being on the two dev panel sessions we did, and having an interesting debate in one of them with Todd on approaches for provisioning - declarative (Features) vs. programmatic (code/PowerShell etc.). This was probably a good lead-in to my talk the next day, and some folks came up to say they really liked this conversation and that we covered it from angles they hadn't considered, which was good to hear. [UPDATE: Photo below of the second session, chaired by &lt;a href="http://www.andrewconnell.com/blog" target="_blank"&gt;AC&lt;/a&gt; and with (from left to right) &lt;a href="http://sharepoint.mindsharpblogs.com/todd" target="_blank"&gt;Todd Bleeker&lt;/a&gt;, &lt;a href="http://www.wildwires.com/" target="_blank"&gt;Stacy Draper&lt;/a&gt;, &lt;a href="http://www.bluedoglimited.com/SharePointThoughts" target="_blank"&gt;Maurice Prather&lt;/a&gt;, &lt;a href="http://www.21apps.com" target="_blank"&gt;Andrew Woodward&lt;/a&gt;, &lt;a href="http://www.sharepointblogs.com/benrobb/default.aspx" target="_blank"&gt;Ben Robb&lt;/a&gt;, &lt;a href="http://www.brettlonsdale.com/" target="_blank"&gt;Brett Lonsdale&lt;/a&gt;, me (with the mic) and &lt;a href="http://www.sharepointcowboy.com/" target="_blank"&gt;Eric Shupps&lt;/a&gt;:]&lt;/p&gt;  &lt;p&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="DevPanel2" src="http://lh3.ggpht.com/_DPonmXhJQ4g/SetYg0TkJwI/AAAAAAAAAZc/cLchMiN-LG4/DevPanel2%5B4%5D.jpg?imgmax=800" width="608" height="457" /&gt; &lt;/p&gt;  &lt;p&gt;So all in all, a top conference, and fantastic to catch up with so many friends. Here's the link for my deck:&lt;/p&gt;  &lt;p&gt;&lt;u&gt;&lt;strong&gt;&lt;a href="http://sharepointchris.googlepages.com/deploymenttalkslidedeck" target="_blank"&gt;Slide deck - Approaches and best practices for deploying SharePoint sites through multiple environments (dev, QA, UAT, production)&lt;/a&gt;         &lt;br /&gt;&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" alt="SBP" src="http://lh5.ggpht.com/_DPonmXhJQ4g/SeUMOdoG6yI/AAAAAAAAAZU/Sz7ubbnr2AA/SBP%5B10%5D.jpg?imgmax=800" width="644" height="121" /&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-4408907796071328100?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/orNmaaKbTvk" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/orNmaaKbTvk/slide-deck-from-my-deployment-talk-at.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/04/slide-deck-from-my-deployment-talk-at.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-7099147167382909551</guid><pubDate>Thu, 26 Mar 2009 00:11:00 +0000</pubDate><atom:updated>2009-03-26T00:12:53.232Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">codeplex</category><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><category domain="http://www.blogger.com/atom/ns#">Content Deployment Wizard</category><title>Command-line support for Content Deployment Wizard now available</title><description>&lt;p&gt;I'm pleased to announce I've now completed initial development on the next version of the Content Deployment Wizard - &lt;strong&gt;this is a beta release for the next few weeks&lt;/strong&gt; so if you need it &amp;quot;just work&amp;quot;, you should continue to use the previous version (1.1), but I'm hoping some people out there are happy to test this beta. The tool has become fairly popular as a 'handy tool to have in the SharePoint toolbox', and hopefully this release extends it's usefulness significantly for some scenarios. If you're not familiar with the tool, it provides a way to import/export site collections, webs, lists, and files or list items, either between farms or between different sites in the same farm - the Codeplex site has more details. As previously mentioned, the key new additional functionality in this release is:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Command-line support &lt;/li&gt;    &lt;li&gt;Support for saving of import/export settings to a file (in the Windows Forms app) for later re-use &lt;/li&gt;    &lt;li&gt;An installer &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Having command-line support for the Wizard means that it can now be used in an automated way. Some key scenarios I think this might be useful in are:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;Continuous integration/automated builds&lt;/strong&gt; - if your site relies on SharePoint content, you can now move 'real' data as part of a build process, copying selected content from 'dev' to 'build' or 'test' for example. I often see &lt;em&gt;static&lt;/em&gt; data (perhaps from an XML file or Excel spreadsheet) used in this way in nAnt/CruiseControl/MSBuild scripts, but for frequently changing data (config values, lookup lists etc.), this doesn't work so well as there is always a static file to maintain separately.&amp;#160; &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Deployment scripts&lt;/strong&gt; - if you have deployment scripts to 'bootstrap' a website on developer machines, again pulling &lt;em&gt;real&lt;/em&gt; data from a central 'repository site' can help here. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;As part of a production 'Content Deployment strategy'&lt;/strong&gt; - since out-of-the-box Content Deployment is restricted to deploying a web as the smallest item, the Wizard could be used to deploy selected lists/list items/files &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Obviously you might have your own ideas about where it could slot into your processes too. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;How it works&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;First, we select the content to move as we would normally using the Wizard..      &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://lh4.ggpht.com/_DPonmXhJQ4g/Scq7mnljgyI/AAAAAAAAAYg/zGbdtI2kmhc/s1600-h/SelectExportItems%5B8%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="SelectExportItems" src="http://lh3.ggpht.com/_DPonmXhJQ4g/Scq6T9zVHFI/AAAAAAAAAYk/aDhE7ORAEyE/SelectExportItems_thumb%5B4%5D.jpg?imgmax=800" width="754" height="571" /&gt;&lt;/a&gt;       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;..and select the options we want to use for this export..      &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://lh5.ggpht.com/_DPonmXhJQ4g/Scq6UuAcFMI/AAAAAAAAAYs/3VWYRAR7oR4/s1600-h/SelectExportSettings%5B8%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="SelectExportSettings" src="http://lh3.ggpht.com/_DPonmXhJQ4g/Scq6VYlTijI/AAAAAAAAAY0/lvSVVLsoV4Y/SelectExportSettings_thumb%5B4%5D.jpg?imgmax=800" width="754" height="570" /&gt;&lt;/a&gt;&amp;#160; &lt;/li&gt; &lt;br /&gt;   &lt;li&gt;On the final screen, the new 'Save settings..' button should be used to save your selections to an XML file:&amp;#160; &lt;br /&gt;&lt;br /&gt;&lt;a href="http://lh5.ggpht.com/_DPonmXhJQ4g/Scq89HJ-3yI/AAAAAAAAAZM/myohwIqECcc/s1600-h/SaveSettingsButton%5B13%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="SaveSettingsButton" src="http://lh3.ggpht.com/_DPonmXhJQ4g/Scq89wS9GjI/AAAAAAAAAZQ/3Ir0nTytnOo/SaveSettingsButton_thumb%5B7%5D.jpg?imgmax=800" width="754" height="570" /&gt;&lt;/a&gt;&amp;#160;&amp;#160; &lt;br /&gt;This will then give you an XML file which looks like this:&lt;/li&gt;    &lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;ExportSettings&lt;/span&gt; &lt;span style="color: #ff0000"&gt;SiteUrl&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;http://cob.publish.dev&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ExcludeDependencies&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;False&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ExportMethod&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;ExportAll&amp;quot;&lt;/span&gt; &lt;/pre&gt; &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                &lt;span style="color: #ff0000"&gt;IncludeVersions&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;LastMajor&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;IncludeSecurity&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;None&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;FileLocation&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;C:\Exports&amp;quot;&lt;/span&gt; &lt;/pre&gt;&lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;                &lt;span style="color: #ff0000"&gt;BaseFileName&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;BlogSubwebAndPageTemplates.cmp&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;      &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;ExportObjects&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;      &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;DeploymentObject&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Id&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;b0fd667b-5b5e-41ba-827e-5d78b9a150ac&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Title&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Blog&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Url&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;http://cob.publish.dev/Blog&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Type&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Web&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;IncludeDescendants&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;All&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;      &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;DeploymentObject&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Id&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;cfcc048e-c516-43b2-b5bf-3fb37cd561be&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Title&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;http://cob.publish.dev/_catalogs/masterpage/COB.master&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Url&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;_catalogs/masterpage/COB.master&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Type&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;File&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;IncludeDescendants&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;None&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;    &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;    &lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;DeploymentObject&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Id&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;670c1fb3-12f3-418b-b854-751ba80da917&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Title&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;http://cob.publish.dev/_catalogs/masterpage/COBLayoutSimple.aspx&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Url&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;_catalogs/masterpage/COBLayoutSimple.aspx&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Type&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;File&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;IncludeDescendants&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;None&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;     &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;  &lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;ExportObjects&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;     &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #800000"&gt;ExportSettings&lt;/span&gt;&lt;span style="color: #0000ff"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;    &lt;/div&gt;  &lt;/div&gt;&lt;br /&gt;  &lt;li&gt;So we now have an XML 'Wizard deployment settings file' which has the IDs of the objects we selected and the export options. We'll go ahead and show how this can be used at the command-line, but note also these settings can also be loaded into the Wizard UI on future deployments to save having to make the selections again - the key is the 'Load settings..' button on the first page (which we didn't show earlier):&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_DPonmXhJQ4g/Scq6Y0vIJYI/AAAAAAAAAYQ/qMqcBNRB4mI/s1600-h/LoadSettingsButton%5B7%5D.jpg"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" alt="LoadSettingsButton" src="http://lh5.ggpht.com/_DPonmXhJQ4g/Scq6Zv7czrI/AAAAAAAAAYU/Kwd_cGoEyGI/LoadSettingsButton_thumb%5B3%5D.jpg?imgmax=800" width="802" height="606" /&gt;&lt;/a&gt;&amp;#160; &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;For command-line use of the Wizard a custom STSADM command is used. We pass the settings file in using the -settingsFile switch. To run the export operation we showed above, our command would look like: &lt;br /&gt;    &lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;   &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;      &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;stsadm -o RunWizardExport -settingsFile &amp;quot;C:\DeploymentSettings\ExportBlogSubwebAndTemplates.xml&amp;quot; -quiet&lt;/pre&gt;      &lt;/div&gt;    &lt;/div&gt;The -quiet parameter is optional, and suppresses some of the progress messages which are returned during the operation. &lt;br /&gt;    &lt;br /&gt;&lt;/li&gt;  &lt;li&gt;&lt;strong&gt;For an import operation, we follow the same process - go through the Wizard and select the settings for the import operation, then click 'Save settings..' at the end to get the file&lt;/strong&gt; (N.B. note the 'Import settings' screen has been simplified slightly from previous versions): &lt;br /&gt;    &lt;br /&gt;&lt;a href="http://lh4.ggpht.com/_DPonmXhJQ4g/Scq6aHC5N8I/AAAAAAAAAYY/4RQr-aK3xX8/s1600-h/SelectImportSettings%5B3%5D.jpg"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" alt="SelectImportSettings" src="http://lh5.ggpht.com/_DPonmXhJQ4g/Scq6bVmVZcI/AAAAAAAAAYc/eMqCg6wa6QQ/SelectImportSettings_thumb%5B1%5D.jpg?imgmax=800" width="802" height="606" /&gt;&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;The command to import looks like this: &lt;br /&gt;    &lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;      &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;        &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: white; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;stsadm -o RunWizardImport -settingsFile &amp;quot;C:\DeploymentSettings\ImportBlogSubwebAndTemplates.xml&amp;quot; -quiet&lt;/pre&gt;      &lt;/div&gt;    &lt;/div&gt;So that's both sides of it.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Using it for real&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;In real use of course, you may be deploying from one SharePoint farm to another. In this case, you also need to deal with copying the .cmp file from the source environment to the target if you're going across farms - if you have network access between farms (e.g. you're using it internally for automated builds/CI), a simple XCOPY in your scripts is the recommended way to do this. For production Content Deployment scenarios with no network connectivity, what I'm providing here will need to be supplemented with something else which will deal with the file transport. Clearly something web service based could be the answer.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Summary&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Using the Wizard at the command-line may prove extremely useful if you need to move any SharePoint content regularly in an automated way. In contrast with other ways you might approach this, the XML definition file allows you to choose any number of webs/lists/list items/files to move in one operation, which may suit your needs better than shipping items around separately. &lt;/p&gt;&lt;p&gt;This is very much a beta release, but as a sidenote I'm expecting the initial issues to mainly be around the installer rather than core code - hence I'm providing a 'manual' install procedure which will get you past any such issues (see the readme). Needless to say, all the source code is also there for you on Codeplex if you're a developer happy to get your hands dirty. As I say, I'm hoping a couple of friendly testers will try it out and help me iron out the wrinkles - please submit any issues to the Codeplex site linked to below.&lt;/p&gt;&lt;p&gt;You can download the 2.0 beta release of the Wizard (and source code) from:&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;Wizard 2.0 beta release - &lt;a href="http://spdeploymentwizard.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=25271"&gt;http://spdeploymentwizard.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=25271&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;Wizard Codeplex site homepage - &lt;a href="http://spdeploymentwizard.codeplex.com"&gt;http://spdeploymentwizard.codeplex.com&lt;/a&gt; &lt;/li&gt;&lt;/ul&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-7099147167382909551?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/O47-v6L_H8E" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/O47-v6L_H8E/command-line-support-for-content.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">20</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/03/command-line-support-for-content.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-7666112726780719173</guid><pubDate>Mon, 09 Mar 2009 22:33:00 +0000</pubDate><atom:updated>2009-03-09T22:33:12.732Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><category domain="http://www.blogger.com/atom/ns#">Content Deployment Wizard</category><title>Update on next version of Content Deployment Wizard</title><description>&lt;p&gt;Generally I only ever talk about SharePoint tools I'm working on once they're 100% complete and ready for use, but recently I had a conversation with someone at a user group which made me think about a policy change. Regular readers will know the main tool I'm associated with is the &lt;a href="http://www.codeplex.com/SPDeploymentWizard"&gt;SharePoint Content Deployment Wizard&lt;/a&gt; which has become fairly popular (over 7000 downloads) - occasionally I've mentioned that one goal was to implement a command-line version, since this opens up all sorts of deployment possibilities. However I've not talked about this for a while, and just recently I've spoken to a couple of people who assumed I dropped this/didn't have the time to look at it, so here I am to tell you this is not the case! &lt;/p&gt;  &lt;p&gt;For anybody that cares, the good news is I've actually been working on this since December interspersed with blogging, and am nearly done. The yicky refactoring work is complete, and I got chance to write the custom STSADM command on the front of it on the flight to the MVP summit last week. I need to do more testing first, but I'm &lt;strong&gt;hoping to release a beta to Codeplex over the next couple of weeks&lt;/strong&gt; - if you're interested in the idea of scripted deployment of specific sites/webs/lists/list items between sites or farms (remember MOSS Content Deployment only does sites/webs and requires HTTP(S) connectivity), I'm hoping some friendly beta testers will help me screw the last bits down. The key aspects of this release are:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Command-line support &lt;/li&gt;    &lt;li&gt;Support for saving of import/export settings to a file (in the Windows Forms app) for later re-use &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Shortly after this release, I'm hoping to add support for incremental deployments (so only the content which has actually changed in the sites/webs/lists/you select will be deployed), but that's not going to make into this next cut unfortunately.&lt;/p&gt;  &lt;p&gt;Keep tuned for further updates :-)&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Other stuff&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Whilst I'm at it, other things in the pipeline from me include:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;Blog article on optimizing WCM sites&lt;/strong&gt; - lots of good guidance on this has been around for a while, but I'm compiling stuff from different sources and highlighting what had a big impact for us on a recent trouble-shooting/optimization piece &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Best Practices presentation&lt;/strong&gt; - as &lt;a href="http://www.sharepointnutsandbolts.com/2009/01/speaking-at-sharepoint-best-practices.html" target="_blank"&gt;previously noted&lt;/a&gt;, I'm speaking at the &lt;a href="http://www.sharepointbestpractices.co.uk/" target="_blank"&gt;European SharePoint Best Practices Conference&lt;/a&gt; in London in April. Happily, I'm now delivering my preferred talk - &amp;quot;&lt;em&gt;&lt;a href="http://www.sharepointbestpractices.co.uk/Abstracts_DEV.html#DE212" target="_blank"&gt;Approaches and best practices for deploying SharePoint sites through multiple environments (dev, QA, UAT, production)&lt;/a&gt;&lt;/em&gt;&amp;quot; so that should be fun &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Needless to say, there are plenty of other blog articles on my 'ideas list' too. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Sidenote - reflecting on 2 years of SharePoint blogging&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Bizarrely, I'm into my 3rd year of SharePoint blogging now. I've no idea how this happened. Having done some interesting work with SharePoint's Feature framework, the initial idea was to write 4 or 5 articles I had material for - as a record for myself more than anything - and be done with it. Since then, although I do write the odd 'easy' post (like this one), generally my articles seem to take a long time to get completed, but I know they could be better. Occasionally I get reminded of this! So there's a long way to go for me to become a better blogger, but I'm fully hoping to still be at it in &lt;em&gt;another&lt;/em&gt; 2 years time - and I'll have plenty more to say when the next version of SharePoint approaches :-)&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-7666112726780719173?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/Gfxw2MV3VAA" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/Gfxw2MV3VAA/update-on-next-version-of-content.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">11</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/03/update-on-next-version-of-content.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-7633559648039747929</guid><pubDate>Tue, 24 Feb 2009 12:41:00 +0000</pubDate><atom:updated>2009-02-24T12:45:13.137Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><category domain="http://www.blogger.com/atom/ns#">user group</category><title>UK user group meeting in London this Thursday, with Q &amp; A panel</title><description>&lt;p&gt;Just a quick note to remind UK-based folks within reach that there is a UK SharePoint user group meeting in London this Thursday. There are two sessions, one of which is an open Q &amp;amp; A for you to bring your trickiest SharePoint questions - I'll be amongst those on the panel representing the developer side of the house, but the line-up will cover all the bases. Needless to say, if you don't get chance to ask your question during the main session, there'll probably be ample opportunity in the pub afterwards. Michael Noel's session also looks extremely interesting, with a whole host of architecture/infrastructure knowledge condensed into one easily-digestible chunk.&lt;/p&gt;  &lt;p&gt;Details from the &lt;a href="http://suguk.org/"&gt;suguk.org&lt;/a&gt; - to sign-up, use the link at the bottom of this post:&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Session 1 - Building the Perfect SharePoint Farm: A Walkthrough of Best Practices from the Field - Michael Noel (&lt;/u&gt;&lt;/strong&gt;&lt;a href="http://www.amazon.co.uk/s/ref=nb_ss_w_h_?url=search-alias%3Daps&amp;amp;field-keywords=michael+noel+sharepoint"&gt;&lt;strong&gt;&lt;u&gt;see books written by Michael&lt;/u&gt;&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;&lt;u&gt;)&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;SharePoint 2007 has proven to be a technology that is remarkably easy to get running out of the box. On the flipside, however, some of the advanced configuration options with SharePoint are notoriously difficult to setup and configure, and a great deal of confusion exists regarding SharePoint best practice design, deployment, disaster recovery, and maintenance. This session covers best practices encompassing the most commonly asked questions regarding SharePoint infrastructure and design, and includes a broad range of critical but often overlooked items to consider when architecting a SharePoint environment. In short, all of the specifics required to build the 'perfect' SharePoint farm are presented through discussion of real-world SharePoint designs of all sizes.    &lt;br /&gt;&amp;#8226; Learn from previous real world deployments and avoid common mistakes.     &lt;br /&gt;&amp;#8226; Plan a checklist for architecture of SharePoint environments of any size.     &lt;br /&gt;&amp;#8226; Build the 'perfect' SharePoint farm for your organization.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Session 2 - SharePoint Q &amp;amp; A Session&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Following the session from last year we thought it would be a good idea to have a session where you can bring your SharePoint problems and hassles to and we can debate them as a group. We'll have a whiteboard, a laptop, and lots of clever people to discuss your questions and issues - so bring along your best and toughest!    &lt;br /&gt;    &lt;br /&gt;The meeting is hosted at Microsoft in Victoria - &lt;strong&gt;&lt;u&gt;arrive 6pm for a 6:30pm start&lt;/u&gt;:       &lt;br /&gt;&lt;/strong&gt;    &lt;br /&gt;Microsoft London (Cardinal Place)     &lt;br /&gt;100 Victoria Street     &lt;br /&gt;London SW1E 5JL     &lt;br /&gt;Tel: 0870 60 10 100 &lt;/p&gt;  &lt;p&gt;To register, simply reply to this thread leaving your &lt;em&gt;full&lt;/em&gt; name - &lt;a title="http://suguk.org/forums/thread/16904.aspx" href="http://suguk.org/forums/thread/16904.aspx"&gt;http://suguk.org/forums/thread/16904.aspx&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Look forward to your questions :-)&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-7633559648039747929?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/9zdpAWAnFoM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/9zdpAWAnFoM/uk-user-group-meeting-in-london-this.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/02/uk-user-group-meeting-in-london-this.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-2583418333395350183</guid><pubDate>Tue, 10 Feb 2009 19:24:00 +0000</pubDate><atom:updated>2009-02-11T22:52:46.884Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><category domain="http://www.blogger.com/atom/ns#">webparts</category><title>Extending the web part framework - part 2</title><description>&lt;p&gt;In part 1, I showed &lt;a href="http://www.sharepointnutsandbolts.com/2009/01/extending-web-part-framework-part-1.html" target="_blank"&gt;how we implemented a 'toolbox' of page templates and functionality modules wrapped up in a governance framework&lt;/a&gt;, to fulfil our client's requirement of a flexible WCM platform for building 80-100 internet sites with varying requirements. In this post, I want to detail some of the &lt;strong&gt;issues&lt;/strong&gt; we ran into and the &lt;strong&gt;resolutions&lt;/strong&gt; we found, focusing primarily on the 'module framework' we developed which is heavily-oriented around SharePoint web parts.&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Quick recap&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;The client is a large multi-national enterprise, and the idea is that content authoring teams in 80-100 countries will take what we've delivered on MOSS to create their country's internet presence e.g. .com, .co.uk, .fr, .es etc., replacing the existing mish-mash of sites on different technologies with inconsistent branding/look and feel.&lt;/p&gt;  &lt;p&gt;In terms of the module framework, the cornerstones of our implementation were (see &lt;a href="http://www.sharepointnutsandbolts.com/2009/01/extending-web-part-framework-part-1.html" target="_blank"&gt;part 1&lt;/a&gt; for more complete details on these):&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;&lt;strong&gt;Module matrix - &lt;/strong&gt;rules for which module can be used where, to guide authors away from building a user experience which doesn't&amp;#160; 'make sense'&lt;strong&gt; &lt;/strong&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;SmartPart-like approach, but with web part properties - &lt;/strong&gt;web parts wrapping user controls but also supporting web part properties exposed in &lt;a href="http://www.15seconds.com/Issue/040427.htm" target="_blank"&gt;custom tool parts&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Base web part/base tool part class - &lt;/strong&gt;responsible for 'framework' behaviour such as checking if the current web part can be added (according to the module matrix)&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Combine interface of publishing field controls with web part storage - &lt;/strong&gt;since publishing field controls (e.g. RichHtmlField) must be added in a 'static' manner at design-time but our authors can add controls dynamically at run-time, we developed custom controls which combine the rich functionality of the publishing HTML editor with web part storage&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Control adapter for WebPartZone for accessibility compliance &lt;/strong&gt;- to get round the problem of all the HTML tables generated by SharePoint's web part framework, which will prevent a site validating for AA&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Present only &lt;em&gt;our&lt;/em&gt; web parts in the web part picker &lt;/strong&gt;- since standard SharePoint web parts are not used anywhere in these sites&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Remove unnecessary options when editing web part properties (tool parts) &lt;/strong&gt;- to avoid confusing the authors&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Issues and resolutions&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;I think that many of the challenges we faced are worth sharing as they came about through general web part development, rather than anything specific to what we did. Before I detail the actual gotchas, take note of some key development characteristics of our project:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Solutions and features used to deploy artifacts such as page layouts, content types etc.&lt;/li&gt;    &lt;li&gt;Kivati Studio used for some other deployment aspects&lt;/li&gt;    &lt;li&gt;Main functionality implemented in user controls - web parts were effectively thin wrappers around the .ascx files using LoadControl()&lt;/li&gt;    &lt;li&gt;Web parts which are 'mandatory' are added to pages using the &lt;a href="http://msdn.microsoft.com/en-us/library/ms480534.aspx" target="_blank"&gt;AllUsersWebPart&lt;/a&gt; element in a feature (though as the points probably illustrate, we looked at numerous ways of dealing with this)&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;u&gt;&lt;strong&gt;Finding #1 - web parts outside of web part zones cannot be edited&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;The reason we wanted to have web parts outside of zones (perfectly possible by dragging a web part directly into page layout markup in SharePoint Designer) is for 'fixed' page modules which could not be removed by the content author. When we placed web parts outside of web part zones, we found the web parts would run fine in presentation mode but unfortunately cannot be edited (e.g. to edit web part properties) - the edit menu for the web part simply does not appear. I speculate this is because it is web part &lt;em&gt;zones &lt;/em&gt;which are linked to web part storage, and thus web part properties cannot be persisted without a zone (the values in the markup will always be used). Hence, if you want editable web parts, you need web part zones.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Resolution -&lt;/strong&gt; ensure all web parts (even ones which cannot be removed) live in a web part zone.&lt;/p&gt;  &lt;p&gt;&lt;u&gt;&lt;strong&gt;Finding #2 - embedding web parts into user control markup appears to be problematic&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;We tested various permutations of using web parts in/out of web part zones, and also with the HTML markup directly in the page layout .aspx or in a child .ascx file. After establishing that web part zones were required, we also found that whether the markup was in the .aspx or .ascx appeared to make a difference. This was unexpected, but the net effect seems to be that if you insert the web part markup into a web part zone which is in a user control rather than directly in the page layout .aspx (i.e. by refactoring the HTML markup for the web part zone and it's contents into a user control), again the edit menu will not display. I'm not sure why this is, but it could be related to the page execution lifecycle.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Resolution -&lt;/strong&gt; accept that if web part zones will have web parts added to them at design-time by markup, the web part zone declaration cannot be in a user control.&lt;/p&gt;  &lt;p&gt;&lt;u&gt;&lt;strong&gt;Finding #3 - when using AllUsersWebPart element, duplicate web parts appear if the feature containing your page layouts is reactivated&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;Having decided our 'fixed' web parts would be added to pages using the &lt;a href="http://msdn.microsoft.com/en-us/library/ms480534.aspx" target="_blank"&gt;AllUsersWebPart&lt;/a&gt; feature element (N.B. using this approach, 'default' web parts are associated with page layouts in the feature which deploys them. Web part zones are left empty on the page layout, and SharePoint provisions the web part into the zone at the time of creating a page from the layout). The issue we had with this is that all the web parts in all the zones in existing pages would be &lt;em&gt;duplicated&lt;/em&gt; if the page layout feature was reactivated - this is because this XML is used both when the feature is activated (in the same way as say, provisioning for content types happens on activation) but also when new pages are created from a page layout. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Resolution -&lt;/strong&gt; write a script (a Kivati task in our case) to remove duplicate web parts across all sites&lt;/p&gt;  &lt;p&gt;[UPDATE - Waldek has an elegant solution to this problem in &lt;a href="http://blog.mastykarz.nl/preventing-provisioning-duplicate-web-parts-instances-on-feature-reactivation/" target="_blank"&gt;'Preventing provisioning duplicate Web Part instances on Feature reactivation'&lt;/a&gt;, as well as sample code similar to what we wrote for our script. DOH!]&lt;/p&gt;  &lt;p&gt;&lt;u&gt;&lt;strong&gt;Finding #4 - duplicate web parts can also appear when the page layout is customized (ghosted)&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;I'm not exactly clear on the reasons why customized files would ever cause duplicate web parts to appear, but that's certainly what we seemed to find. What happened is that we would deploy our master pages/page layouts using a feature to our QA environment, but immediately these files would be provisioned in that site as &lt;em&gt;customized&lt;/em&gt; (i.e. the content in the content database), instead of being uncustomized and referenced on the filesystem. After further investigation, we traced the cause of this unexpected behaviour to the use of these attributes SPD adds to page layouts:&lt;/p&gt;  &lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;   &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;meta:progid=&amp;#8221;SharePoint.WebPartPage.Document&amp;#8221; meta:webpartpageexpansion=&amp;#8221;full&amp;#8221;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Resolution - &lt;/strong&gt;ensure the version of the file does not contain these attributes. We actually switched to running uncustomized master page/page layouts even in our development farm. This means that we deployed the files using a feature and thereafter never opened them in SPD (editing only the source-controlled feature file instead). &lt;/p&gt;&lt;p&gt;&lt;u&gt;&lt;strong&gt;Finding #5 - avoid setting default properties in the web part definition file (.webpart)&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;&lt;p&gt;A final lesson we learnt is that, when working with web parts it's often better to &lt;em&gt;avoid&lt;/em&gt; using the .webpart definition file extensively for setting default property values. There's nothing wrong with the mechanism - effectively these values are read whenever the web part is provisioned on a page, and your instance will set it's properties to these values. The problem, of course, is when you realize a property value you defined in the .webpart file needs to be updated because something changed. What happens to all the existing instances on pages around your site? As you might guess, the answer is nothing - unless you take steps to update those also, which generally means writing some kind of script to use SPLimitedWebPartManager. This can be pretty inconvenient when all you wanted to do was quickly change a default value. &lt;/p&gt;&lt;p&gt;&lt;strong&gt;Resolution -&lt;/strong&gt; consider ensuring .webpart files are stripped to the bare minimum (assembly name etc.) and configuration comes from somewhere else. We typically rolled these config items into our use of the &lt;a href="http://www.codeplex.com/SPConfigStore/" target="_blank"&gt;Config Store&lt;/a&gt;. &lt;/p&gt;&lt;br /&gt;&lt;strong&gt;&lt;u&gt;Summary &lt;/u&gt;&lt;/strong&gt;&lt;br /&gt;&lt;p&gt;We ran into a few unexpected gotchas when building on the web part framework, but steps can be taken to minimise their impact. Hope you find these useful if you do web part development. Special thanks to Karoly Szalkary for helping to refresh my memory on some of these!&lt;/p&gt;&lt;br /&gt;&lt;p&gt;P.S. After 2 years writing about it, I've decided I no longer need to capitalize the 'f' in 'feature' - I think we're all on the same page on that one now ;-) &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-2583418333395350183?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/MGti_cuvZg0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/MGti_cuvZg0/extending-web-part-framework-part-2.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/02/extending-web-part-framework-part-2.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-1923720533355381689</guid><pubDate>Sat, 31 Jan 2009 23:31:00 +0000</pubDate><atom:updated>2009-02-01T18:42:27.289Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><category domain="http://www.blogger.com/atom/ns#">webparts</category><title>Extending the web part framework - part 1</title><description>&lt;p&gt;Today I want to show some of the interesting things we've been doing with web parts for one of our clients. There's quite a lot to talk about so it will be over two articles:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Part 1 - background and implementation&lt;/li&gt;    &lt;li&gt;Part 2 - issues and resolutions&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;There are a couple of things in particular which I think are quite cool, as we've effectively combined classic WCM (publishing) site functionality with a customized implementation of the web part framework. The context is a fairly large roll-out to an enterprise client, but what we're rolling out is a centralized platform for 80-100 &lt;em&gt;internet&lt;/em&gt; sites. The idea is that content authoring teams in 80-100 countries will take what we've delivered on MOSS to create their own sites - replacing the existing mish-mash of sites on different technologies with inconsistent branding/look and feel.&lt;/p&gt;  &lt;p&gt;Clearly a key challenge here is satisfying the diverse needs of so many stakeholders. So a cornerstone of the platform is that sites can be tailored somewhat, so each country has some flexibility to communicate with their audience in the way they think is best. We effectively give the authors a set of page templates and building blocks, and a system which governs how the blocks can fit together so that the user experience will still 'make sense'. Needless to say, a lot of analysis and consideration has gone into this - both in terms of what functionality was needed but also user journeys and navigation through the site, and the experience architects on our side (LBi) played a vital role here. There are many aspects to the project I could zone in on, but since I want to focus on the implementation details here, I'll &lt;em&gt;briefly&lt;/em&gt; list some of these building block requirements before showing how we did it.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Key requirements/challenges:&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;In order to create the different page types, we needed around 15 &lt;strong&gt;page layouts&lt;/strong&gt;, including these:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Home page &lt;/li&gt;    &lt;li&gt;Channel hub/Alternative hub/Sub home - these are different template options for '2nd and 3rd level' pages&amp;#160; &lt;/li&gt;    &lt;li&gt;Content page &lt;/li&gt;    &lt;li&gt;Product page &lt;/li&gt;    &lt;li&gt;Media release &lt;/li&gt;    &lt;li&gt;List - provides links to a series of related pages &lt;/li&gt;    &lt;li&gt;Etc. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;And whilst some aspects of page functionality was 'fixed' on the template, there were many other items which were optional - these were to be added to pages by the authors, either in a 'web part' kind of way or perhaps something else. Some examples of these optional &lt;strong&gt;'page modules'&lt;/strong&gt; were:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;'Hero' feature - used to highlight something on prominent pages with an image/flash/text &lt;/li&gt;    &lt;li&gt;Right-hand promo &lt;/li&gt;    &lt;li&gt;Content editor module - allows an author to enter arbitrary content, but for reasons which will become clearer we developed an interesting custom control which is kind of a cross between a publishing HtmlField and a Content Editor web part (covered later) &lt;/li&gt;    &lt;li&gt;Generic content module - rolls-up formatted content/links to a selected page &lt;/li&gt;    &lt;li&gt;List/tabbed list - provides links to a series of related pages &lt;/li&gt;    &lt;li&gt;Dynamic share price - displays latest stock price based on web service call &lt;/li&gt;    &lt;li&gt;Product selector - using AJAX cascading dropdowns to filter products &lt;/li&gt;    &lt;li&gt;Etc. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Although there were lots of other challenges (such as &lt;a href="http://www.sharepointnutsandbolts.com/2008/12/building-multi-lingual-sharepoint-sites.html" target="_blank"&gt;multi-lingual content&lt;/a&gt;, packaging/documenting every deployment aspect so the hosting company could deploy etc.!), I felt that building the 'framework' could be more challenging than individual functionality bits. To help frame what you'll read next, some initial &lt;strong&gt;questions&lt;/strong&gt; we had for the implementation were:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;How do these optional bits of functionality get added to the page? As web parts, or something else? &lt;/li&gt;    &lt;li&gt;How do we get accessibility-compliance if web parts? &lt;/li&gt;    &lt;li&gt;How do we provide configuration if not web parts? &lt;/li&gt;    &lt;li&gt;How do we restrict which modules can be used where (as per the specification)? &lt;/li&gt;    &lt;li&gt;Since we're in a 'flexible' publishing site, how do we determine which fields are needed on the content types? Does each content type need to have all the possible fields the author might choose to add? &lt;/li&gt;    &lt;li&gt;If we are working with publishing controls, how would we bind the dynamically added control to the 'back-end' publishing field on the content type? &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;The implementation&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;As well as the optional page modules, most of the templates had a classic set of publishing fields. After looking at custom approaches, we concluded the web part framework had a lot going for it for the optional stuff - clearly we could avoid building a user interface to pick the module from a list/add to page/allow configuration of properties specific to the module, and also get drag and drop (amongst other things) as an added bonus. The concept of web part &lt;em&gt;zones -&lt;/em&gt; as a container where one or more modules could be placed - was also important to our page structure.&lt;/p&gt;  &lt;p&gt;Another challenge for the optional modules was where to store the data. If they were publishing fields, we would need every possible module to have a corresponding field on every possible content type, and this was pretty impractical when looking at the spec. Web parts, of course, use a different model and the framework takes care of data storage regardless of how many controls are on the page.&lt;/p&gt;  &lt;p&gt;On the downside, a key thing to remember with web parts in publishing pages is that web part data is (by definition) not stored in publishing fields, and therefore isn't versioned in the same way. After discussing with the client, in our case this proved to not have as big an impact as we initially thought, due to the split and nature of what content would be stored in publishing fields vs. what would be web parts. So, having the client's acceptance of this trade-off, we went with web parts and came up with these solution elements:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;&lt;strong&gt;Module matrix&lt;/strong&gt;       &lt;br /&gt;      &lt;br /&gt;This comprised two SharePoint lists which contained the 'rules matrix', to enforce the design team's specification of what functionality could be used on which page type. Effectively the data provides the mapping of modules and page layouts. Being list data, it meant that it could be easily updated by the central team if a policy change was required. This data was consumed by our base web part (point 4).      &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;SmartPart-like approach, but with web part properties&lt;/strong&gt;      &lt;br /&gt;      &lt;br /&gt;We wanted the actual functionality of our web parts to be implemented in user controls, for the typical reason of avoiding building HTML in C# code (wrong on so many levels!). This is obviously what the SmartPart does using LoadControl(), but we had the additional requirement of needing to pass web part property values to our user controls - this meant we could use the familiar 'tool part' interface (i.e. setting web part properties in the right-hand pane) for control configuration.&amp;#160; &lt;br /&gt;      &lt;br /&gt;In our model, each user control has a corresponding 'wrapper' web part/tool part which understands which properties are required and how to build the properties UI. In the web part's OnInit() method, values are passed from the web part properties to the user control so that the latter is initialized ready to do it's processing.       &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Base web part/base tool part&lt;/strong&gt;      &lt;br /&gt;      &lt;br /&gt;All our web parts/tool parts were derived from our custom classes which abstracted some responsibilities. Since we couldn't easily change the web part picker screen to only display appropriate web parts for the zone the author had selected, we built the check into the base web part - if an 'invalid' web part was added, the web part renders nothing in presentation mode but in edit mode we display a message to the author like this:      &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://lh3.ggpht.com/_DPonmXhJQ4g/SYDhZuKskvI/AAAAAAAAAXI/n9tdHzAE0z8/s1600-h/ModuleNotValidMessage%5B4%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="102" alt="ModuleNotValidMessage" src="http://lh6.ggpht.com/_DPonmXhJQ4g/SYDhaOx9dSI/AAAAAAAAAXM/xUToUqpzeqo/ModuleNotValidMessage_thumb%5B2%5D.jpg?imgmax=800" width="265" border="0" /&gt;&lt;/a&gt;&amp;#160; &lt;br /&gt;      &lt;br /&gt;Adding &lt;em&gt;too many&lt;/em&gt; web parts to a zone (count determined in the module matrix data) would have a similar effect.      &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Combine interface of publishing field controls with web part storage       &lt;br /&gt;&lt;/strong&gt;      &lt;br /&gt;Having decided to use web parts for our control architecture, we had one requirement for something similar to the standard Content Editor web part (CEWP). However, this control is pretty lame compared to the MOSS publishing HtmlField, and we quickly established our client needed more than the basic CEWP. So we combined the bits we wanted from both - the front end control used by the publishing field type (the RichHtmlField control), but the backing store of web part storage rather than a publishing field. This meant authors could add multiple instances of this optional module to their page (and get the nice editing experience), but because it's a web part we didn't need to worry about having a corresponding set of fields on each possible content type. In code/integration terms it's the same approach, but in the end we actually swapped the standard MOSS control for the control which fronts Telerik's RADEditor field since the client wanted to move to this:&amp;#160; &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://lh3.ggpht.com/_DPonmXhJQ4g/SYXqodnAPQI/AAAAAAAAAXg/WYsHxGIuZn0/s1600-h/CustomContentEditorWebPart%5B15%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="612" alt="CustomContentEditorWebPart" src="http://lh5.ggpht.com/_DPonmXhJQ4g/SYXqoxnArII/AAAAAAAAAXk/YNnJnORCjiE/CustomContentEditorWebPart_thumb%5B11%5D.jpg?imgmax=800" width="236" border="0" /&gt;&lt;/a&gt;&amp;#160;&amp;#160; &lt;br /&gt;      &lt;br /&gt;&lt;font color="#000000"&gt;Also note use of another control typically used with publishing fields here, the AssetUrlSelector - this provides the 'Browse...' button shown above, and can be used to provide a friendly way for an author to browse to a file.&lt;/font&gt;      &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Control adapter for WebPartZone for accessibility compliance       &lt;br /&gt;&lt;/strong&gt;      &lt;br /&gt;Since web parts normally render with a stack of nested HTML tables which won't validate against AA, action needs to be taken to remedy this if accessibility is a design goal. However this isn't necessarily a big deal - the approach is that you 'correct' the HTML for the WebPartZone control &lt;em&gt;in presentation mode only, &lt;/em&gt;thus leaving the tables intact in edit mode for all the web part editing framework stuff which needs to happen. You do lose the client-side &lt;a href="http://msdn.microsoft.com/en-us/library/ms477066.aspx" target="_blank"&gt;Web Part Services Component (WPSC)&lt;/a&gt; API doing this, but we had no requirement for it anyway (I rarely see it used). I initially assumed I'd have to write a control adapter to do this, but I found that &lt;a href="http://blog.sharepoint.ch/2007/12/webpartzone-adapter.html" target="_blank"&gt;David Schneider has already done the job&lt;/a&gt; - this works fine. It's also possible the latest version of the &lt;a href="http://aks.hisoftware.com/" target="_blank"&gt;AKS&lt;/a&gt; has one, can't remember if I checked.       &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Present only &lt;em&gt;our&lt;/em&gt; web parts in the web part picker        &lt;br /&gt;&lt;/strong&gt;      &lt;br /&gt;Since this is a highly bespoke WCM platform rather than a standard collaboration environment, we don't want to see any of the standard web parts in the picker for these sites. Two steps to this one:      &lt;br /&gt;      &lt;br /&gt;- delete all the .webpart files from the web part galleries in the sites (N.B. we used Kivati for rolling out such changes across all the site collections - more on this in the future). However, doing this will still leave you with ListView web parts for all the lists/libraries in your site, so you also need to..      &lt;br /&gt;- ensure all your WebPartZone declarations have the little documented 'QuickAdd-ShowListsAndLibraries' property set to false:&lt;font&gt;&lt;/font&gt;      &lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;       &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;WebPartPages:WebPartZone&lt;/span&gt; &lt;span style="color: #ff0000"&gt;id&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;g_AB07678E486C46bc962DFC8446A6CD13&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;runat&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;title&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;Zone 1&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;QuickAdd-ShowListsAndLibraries&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;false&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;    &lt;/div&gt;&lt;br /&gt;    Authors are then not confused by any standard web parts which aren't appropriate for our scenario:&lt;br /&gt;    &lt;br /&gt;&lt;a href="http://lh3.ggpht.com/_DPonmXhJQ4g/SYXqqe0wXkI/AAAAAAAAAXo/giR8qyHhXcg/s1600-h/StrippedWebPartPicker%5B3%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="396" alt="StrippedWebPartPicker" src="http://lh3.ggpht.com/_DPonmXhJQ4g/SYXqrP1VwtI/AAAAAAAAAXs/InNE8a_O7i4/StrippedWebPartPicker_thumb%5B1%5D.jpg?imgmax=800" width="702" border="0" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;  &lt;br /&gt;&lt;li&gt;&lt;strong&gt;Remove unnecessary options when editing web part properties (tool parts) &lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;        Finally, we do a bit of work with the accompanying tool parts (for properties editing) for our web parts to avoid confusing our authors with options which won't take effect. As an example, for a web part which looks like this in presentation mode:&lt;br /&gt;      &lt;br /&gt;&lt;a href="http://lh5.ggpht.com/_DPonmXhJQ4g/SYDhatEeGhI/AAAAAAAAAXQ/9W6_9SY51TY/s1600-h/ProductSelectorModule%5B3%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="319" alt="ProductSelectorModule" src="http://lh4.ggpht.com/_DPonmXhJQ4g/SYDhazz4pgI/AAAAAAAAAXU/C9dgOtaoQoU/ProductSelectorModule_thumb%5B1%5D.jpg?imgmax=800" width="240" border="0" /&gt;&lt;/a&gt;&amp;#160; &lt;br /&gt;&lt;/li&gt;&lt;br /&gt; The tool part looks like this:&lt;br /&gt;            &lt;br /&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="480" alt="ProductSelectorToolPart" src="http://lh5.ggpht.com/_DPonmXhJQ4g/SYDhb1xFCgI/AAAAAAAAAXw/MiPHVMmfn8Q/ProductSelectorToolPart_thumb%5B4%5D.jpg?imgmax=800" width="229" border="0" /&gt;&lt;br /&gt;            &lt;br /&gt;In case you're wondering what to look at, it's that we've removed the standard options SharePoint would normally provide for every web part (such as chrome style etc.), since we want to control these to ensure proper formatting. Normally we'd have these sections at the bottom of the tool part:&lt;br /&gt;            &lt;br /&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="105" alt="RemovedToolPartOptions" src="http://lh3.ggpht.com/_DPonmXhJQ4g/SYXqsJUxDnI/AAAAAAAAAX0/YEMJ1VqtC8U/RemovedToolPartOptions_thumb%5B4%5D.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/font&gt;&lt;/ol&gt;&lt;br /&gt;&lt;strong&gt;&lt;u&gt;Summary&lt;/u&gt;&lt;/strong&gt;&lt;br /&gt;&lt;p&gt;There are many ways SharePoint's web part framework can be extended, and here I'm only showing the path we followed. For a requirement such as our client's, web parts provided a great starting point, perhaps showing there can sometimes be a place for web parts in an accessible publishing site so long as the trade-offs are understood and accepted. &lt;br /&gt;&lt;br /&gt;In part 2 of this series we'll look at issues encountered and their resolutions.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-1923720533355381689?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/osihi7-SDCA" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/osihi7-SDCA/extending-web-part-framework-part-1.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/01/extending-web-part-framework-part-1.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-4425064740160102995</guid><pubDate>Tue, 20 Jan 2009 15:05:00 +0000</pubDate><atom:updated>2009-01-20T15:36:26.046Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">codeplex</category><category domain="http://www.blogger.com/atom/ns#">configuration</category><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><title>A better Config Store for SharePoint sites</title><description>&lt;p&gt;I've recently made some enhancements to my &lt;a href="http://www.codeplex.com/SPConfigStore" target="_blank"&gt;Config Store framework on Codeplex&lt;/a&gt; which I'm now ready to share. Many of these enhancements are a result of adapting the solution to a large project where we built a platform for 80-100 internet sites - hence it's now become a bit more 'enterprise'. With such things I generally make a deal with my employer where I do the work (or most of it) in my spare time and then get to share the code publicly, so here we go. Before I delve into the details, let's have a reminder of what the Config Store is all about:&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Recap - the SharePoint Config Store in a nutshell&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Regular readers may remember this is a solution which allows use of a SharePoint list to store configuration values used by your SharePoint application - the idea is that your webparts/server controls/page layouts etc. store any strings/data they need for configuration in here as a more flexible alternative to web.config or similar. Since our values are now in a SharePoint list, configuration can be updated across the farm &lt;em&gt;through the browser&lt;/em&gt; to administrators who have the appropriate permissions. We can also optionally take advantage of all the other things lists give us such as auditing, item-level security, alerts and version history etc. Finally, a caching layer is used to avoid round trips when your code fetches configuration values.&lt;/p&gt;  &lt;p&gt;On the last couple of projects where my team has used it, we've finished up with 100+ config items in the list - storing all sorts of things from URLs, strings and 'application behaviour' switches:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_DPonmXhJQ4g/SW_H3gZsSwI/AAAAAAAAAXA/nb-rY9U9ts8/s1600-h/ConfigStore5.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="162" alt="ConfigStore" src="http://lh6.ggpht.com/_DPonmXhJQ4g/SW_H4PG-2rI/AAAAAAAAAXE/NR7f5Td9dAs/ConfigStore_thumb3.jpg?imgmax=800" width="640" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;If you need a more complete overview, see:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a title="http://www.sharepointnutsandbolts.com/2008/05/introducing-sharepoint-config-store-for.html" href="http://www.sharepointnutsandbolts.com/2008/05/introducing-sharepoint-config-store-for.html"&gt;http://www.sharepointnutsandbolts.com/2008/05/introducing-sharepoint-config-store-for.html&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a title="http://www.sharepointnutsandbolts.com/2008/05/config-store-faqs.html" href="http://www.sharepointnutsandbolts.com/2008/05/config-store-faqs.html"&gt;http://www.sharepointnutsandbolts.com/2008/05/config-store-faqs.html&lt;/a&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Enhancements in the new release&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;&lt;strong&gt;Optional &amp;quot;hierarchical&amp;quot; configuration model similar to web.config        &lt;br /&gt;        &lt;br /&gt;&lt;/strong&gt;In the first release, since all the config values are stored in one list this means your configuration is stored in one 'nominated' site collection. This is fine for WCM sites which may only use one site collection, but may not easily map to certain enterprise requirements. What's new in this release is that the framework can now use a 'hierarchical' model, where a Config Store list exists in whatever site collections should have one, but a 'master' site collection is nominated which contains the 'master' config values. What happens is that if the config item you request is in the 'local' Config Store you'll get that value, and if not you'll get the value from the master list. This allows local overriding of the parent values if required - in practice we found 95% of the config items would be stored in the master list only, but having the facility to override the other 5% was critical to supporting some of the functionality we developed.       &lt;br /&gt;      &lt;br /&gt;Needless to say, this is the implementation which best suited &lt;em&gt;our&lt;/em&gt; requirements. It could be it doesn't really suit yours, but developers could consider starting with my source code and modifying, since other aspects such as the caching layer, Feature files, event handlers etc. might not need to be changed much.       &lt;br /&gt;      &lt;br /&gt;If you want to continue to just use one Config Store list (even if you consume the config values in multiple site collections), the new code will continue to work just fine for this model too.     &lt;br /&gt;     &lt;br /&gt;   &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Easier to use in ASPX markup &lt;/strong&gt;      &lt;br /&gt;&lt;br /&gt;&lt;font color="#000000"&gt;Similar to my recent Language Store framework, the Config Store now supports easily dropping values into ASPX markup by implementing an expression builder. So if all you want to do is set the Text property of a control, you can now do with this without cluttering up the code-behind. Or, as an alternative example, here we're retrieving an URL from the Config Store (stored in Category 'PageUrls' and key 'MyAccountPage') to assign to a hyperlink:&lt;/font&gt;       &lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;       &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;asp:HyperLink&lt;/span&gt; &lt;span style="color: #ff0000"&gt;id&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;hyperlink1&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;NavigateUrl&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;&amp;lt;%$ SPConfigStore:PageUrls|MyAccountPage %&amp;gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: #ff0000"&gt;Text&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;My account&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;ImageUrl&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;images/pict.jpg&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;runat&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;    &lt;/div&gt;&lt;br /&gt;&lt;/li&gt;  &lt;li&gt;&lt;strong&gt;Fixed caching bug for farm environments &lt;/strong&gt;&lt;br /&gt;    &lt;br /&gt;Yes, something I have to hold my hands up to here - the caching layer in the initial release didn't adequately deal with multiple servers. The effect was that it required an app pool recycle to pick up changes to config values, rather than them taking effect immediately. Not the end of the world, but certainly inconvenient and taking away some of the benefits of using a SharePoint list for configuration. So in this release, the implementation correctly relies on a back-end resource (using a &lt;a href="http://msdn.microsoft.com/en-us/library/system.web.caching.cachedependency.aspx" target="_blank"&gt;CacheDependency&lt;/a&gt;) to invalidate the cache across all servers in the farm. The implementation I chose was to add the items to the cache with a CacheDependency on a text file - this needs to be located on a path which all servers can access - and the event handler now updates a timestamp in the file to invalidate the item in the cache across all servers. &lt;br /&gt;    &lt;br /&gt;I went with using a file as the dependency item as I thought it was more lightweight than using a &lt;a href="http://msdn.microsoft.com/en-us/library/system.web.caching.sqlcachedependency.aspx" target="_blank"&gt;SqlCacheDependency&lt;/a&gt; - I didn't really want to impose the SQL configuration etc. to be able to use the Config Store. However, with the file cache dependency I've chosen, be aware that some production configurations may have internal firewalls which prevent all WFEs from accessing a shared file in this way - check before you deploy. Of course the code is there for you to modify should you wish to make changes in this area.&lt;br /&gt; &lt;/li&gt;  &lt;br /&gt;&lt;li&gt;&lt;strong&gt;Amendment to Feature to prevent web.config modifications being made on Feature activation &lt;/strong&gt;&lt;br /&gt;    &lt;br /&gt;Another thing that got in the way when I tried to implement the Config Store on our enterprise project was the Feature event receiver which adds the required appSettings keys to web.config. The idea here is that, to help simplify installation and initial setup, the required web.config entries are added with empty values so all the developer has to do is plug in the appropriate values for his/her site. Sounds great - and it is for single site collection sites. But for multiple site collections, when we come to activate the Feature in the 2nd, 3rd, nth site collection - of course the receiver runs and adds the empty entries to web.config &lt;em&gt;again&lt;/em&gt;, despite the fact that we'd already inserted the real values on the first activation. And since the new values come later in the file, guess which ones are used? &lt;br /&gt;    &lt;br /&gt;So in this release there's a Feature property which determines if web.config modifications are made - it's set to 'False' by default but it's there if you prefer to change it.&lt;br /&gt;    &lt;br /&gt;&lt;/li&gt;  &lt;li&gt;&lt;strong&gt;'Config value' column is now bigger&lt;br /&gt;    &lt;br /&gt;&lt;/strong&gt;Previously this column was a 'single line of text' but it's now a 'note'. This means you can happily store HTML/XML fragments or other larger values, which is very useful in some scenarios. However, note that if you're already using the Config Store on your site and want to 'upgrade', this schema change is significant - I discuss it in the readme.txt file.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;p&gt;So that's it. You can download the updates from &lt;a href="http://www.codeplex.com/SPConfigStore"&gt;www.codeplex.com/SPConfigStore&lt;/a&gt; - hope you find it as useful as we have. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-4425064740160102995?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/tBoqyJC_WZA" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/tBoqyJC_WZA/better-config-store-for-sharepoint.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">15</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/01/better-config-store-for-sharepoint.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-4701094200451096877</guid><pubDate>Tue, 06 Jan 2009 23:32:00 +0000</pubDate><atom:updated>2009-01-06T23:32:54.105Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">speaking</category><title>Speaking at the SharePoint Best Practices conference</title><description>&lt;p&gt;By now you've probably seen a few posts on the Best Practices&amp;#8482; SharePoint Conferences which are coming up - I'll be speaking at the European 'edition' which is being held 6th - 8th April in London, UK. I submitted a couple of sessions, but here's the one which got confirmed:&lt;/p&gt;  &lt;blockquote&gt;   &lt;h5&gt;Customizing SharePoint the supported way: from end-user to admin interfaces&lt;/h5&gt;    &lt;p&gt;One of the key concepts SharePoint developers must remember is that modifications to core shipped files are typically unsupported. This session looks at ways in which you can implement the kind of customizations your users may ask for without doing anything naughty. Aspects covered include custom site definitions, modifying the site administration/central administration areas, and changing the user controls SharePoint uses with the DelegateControl architecture. The session is packed with demos, and also contains tips on the best way to make other common customizations not shown in the detailed examples.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;I'll probably be off his Christmas card list for saying this, but when Steve Smith (fellow UK MVP and conference organiser) mentioned a while back that he wanted to put something like this on and I should think of sessions, I kind of assumed he meant something with the usual UK suspects, a bit like a user group meeting on steroids. It was only when the site went up I saw how wrong I was! Many of the world's biggest SharePoint names are on the &lt;a href="http://www.sharepointbestpractices.co.uk/Event_Speakers.html" target="_blank"&gt;speaker list&lt;/a&gt; - the developer track has Andrew Connell, Todd Bleeker, Eric Shupps, Maurice Prather (ex SharePoint product group) and fellow UK SharePoint MVPs Andrew Woodward and Ben Robb. And that's not all - there are 4 tracks in total and I've already noticed some non-dev sessions I'd like to get to from experts such as Spence, Mike Watson, Ben Curry and others. The keynote speaker is Joel Oleson. Regardless of your level with SharePoint, for my money there is huge potential for pushing your SharePoint capability forward at this conference. &lt;/p&gt;  &lt;p&gt;As an aside, the other session I submitted (which I really wanted to do!) was this:&lt;/p&gt;  &lt;blockquote&gt;   &lt;h5&gt;Approaches and best practices for deploying SharePoint sites through multiple environments (dev, QA, UAT, production)&lt;/h5&gt;    &lt;p&gt;Solutions and Features, STSADM export, the Content Deployment API &amp;#8211; all can be used to move SharePoint site artifacts from one environment to another, and all have different capabilities and limitations. Failing to plan your deployment processes properly can add significant risk to a SharePoint development project, and in some scenarios the problems may only become apparent when initial development is complete and updates need to be released. In this session we&amp;#8217;ll explore the nuts and bolts of each development/deployment approach, and you&amp;#8217;ll emerge on a firmer footing in this complex area of SharePoint. We&amp;#8217;ll also touch on 3&lt;sup&gt;rd&lt;/sup&gt;-party tools which can help with the deployment puzzle, from Codeplex utilities such as the Content Deployment Wizard to emerging tools such as Kivati Studio.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;If you're thinking of attending and would be interested in this topic, leave me a comment and if there's a good response I'll try and persuade the organisers to shoehorn me in somewhere (perhaps in one of the lunchtime sessions which haven't yet been announced). &lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.sharepointbestpractices.co.uk" target="_blank"&gt;European Best Practices&amp;#8482; SharePoint Conference&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Look forward to seeing you there!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-4701094200451096877?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/KgC5Mk5y2Gw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/KgC5Mk5y2Gw/speaking-at-sharepoint-best-practices.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2009/01/speaking-at-sharepoint-best-practices.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-5836543560406907114</guid><pubDate>Tue, 16 Dec 2008 20:59:00 +0000</pubDate><atom:updated>2008-12-16T21:05:29.123Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">codeplex</category><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><title>Using .Net Expression Builders to set control properties</title><description>&lt;p&gt;In my last post I introduced my &lt;a href="http://www.sharepointnutsandbolts.com/2008/12/building-multi-lingual-sharepoint-sites.html" target="_blank"&gt;Language Store solution for multi-lingual SharePoint sites&lt;/a&gt;, and showed the two ways it can be used:&lt;/p&gt; &lt;p&gt;&lt;u&gt;&lt;strong&gt;In standard .Net procedural code:&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt; &lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;   &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;string&lt;/span&gt; sButtonText = LanguageStore.GetValue(&lt;span style="color: #006080"&gt;&amp;quot;Search&amp;quot;&lt;/span&gt;, &lt;span style="color: #006080"&gt;&amp;quot;SearchGoButtonText&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Declaratively in HTML:&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;&lt;br /&gt;  &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;asp:Button&lt;/span&gt; &lt;span style="color: #ff0000"&gt;runat&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;id&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;btnSearch&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;Text&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;&amp;lt;%$ SPLang:Search|SearchGoButtonText %&amp;gt;&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;This declarative syntax is very useful as it means the developer doesn't have to clutter up code-behind files just to call the method to retrieve a value, then assign it to the 'Text' property of various controls. I've also retrofitted this to my &lt;a href="http://www.codeplex.com/SPConfigStore" target="_blank"&gt;Config Store solution&lt;/a&gt; (along with some other enhancements) and this will be available on Codeplex soon. You might notice it's the same syntax as the &lt;a href="http://www.sharepointblogs.com/benrobb/archive/2007/04/20/how-to-create-an-img-tag-relative-to-the-current-site-collection.aspx" target="_blank"&gt;SPUrl token&lt;/a&gt; which can be used in master pages/page layouts to get a relative path to an image or CSS file, and that's because I'm using the same .Net technique. Since I had to do some digging to work out how this was done, I'm guessing (could be wrong here!) many other developers haven't come across this either, so here's how it's done.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Implementing an expression builder class&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;An expression builder is essentially a class which derives from System.Web.Compilation.ExpressionBuilder and contains logic to evaluate an expression at page parse time. The 'secret' is that the ASP.Net page parsing engine understands that it needs to call the class's method whenever it encounters an expression in the appropriate form. These are the things that join this mini-framework together:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Class derived from ExpressionBuilder which overrides the EvaluateExpression() and GetCodeExpression() methods&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Declaration in web.config which associates your prefix ('SPLang' in my case) with your expression builder class &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Optional use of ExpressionPrefix attribute on class for designer support&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;An expression in declarative HTML (as per the example above)&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;p&gt;Taking things step-by-step, here's what my class looks like:&lt;/p&gt;&lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;&lt;br /&gt;  &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;[ExpressionPrefix(&lt;span style="color: #006080"&gt;&amp;quot;SPLangStore&amp;quot;&lt;/span&gt;)]&lt;br /&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; LangStoreExpressionBuilder : ExpressionBuilder&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; TraceSwitch traceSwitch = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; TraceSwitch(&lt;span style="color: #006080"&gt;&amp;quot;COB.SharePoint.Utilities.LanguageStore&amp;quot;&lt;/span&gt;,&lt;br /&gt;        &lt;span style="color: #006080"&gt;&amp;quot;Trace switch for Language Store&amp;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; LangStoreTraceHelper trace = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; LangStoreTraceHelper(&lt;span style="color: #006080"&gt;&amp;quot;COB.SharePoint.Utilities.LangStoreExpressionBuilder&amp;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;object&lt;/span&gt; GetEvalData(&lt;span style="color: #0000ff"&gt;string&lt;/span&gt; expression, Type target, &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; entry)&lt;br /&gt;    {&lt;br /&gt;        trace.WriteLineIf(traceSwitch.TraceVerbose, TraceLevel.Verbose, &lt;span style="color: #006080"&gt;&amp;quot;GetEvalData(): Entered with expression '{0}'.&amp;quot;&lt;/span&gt;,&lt;br /&gt;            expression);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;string&lt;/span&gt;[] aExpressionParts = expression.Split(&lt;span style="color: #006080"&gt;'|'&lt;/span&gt;);&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; sCategory = aExpressionParts[0];&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; sTitle = aExpressionParts[1];&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; ((aExpressionParts.Length != 2) || (&lt;span style="color: #0000ff"&gt;string&lt;/span&gt;.IsNullOrEmpty(sCategory) || &lt;span style="color: #0000ff"&gt;string&lt;/span&gt;.IsNullOrEmpty(sTitle)))&lt;br /&gt;        {&lt;br /&gt;            trace.WriteLineIf(traceSwitch.TraceError, TraceLevel.Error, &lt;span style="color: #006080"&gt;&amp;quot;GetEvalData(): Unable to parse expression '{0}' into &amp;quot;&lt;/span&gt; +&lt;br /&gt;            &lt;span style="color: #006080"&gt;&amp;quot;format 'Category|Title' - throwing exception.&amp;quot;&lt;/span&gt;,&lt;br /&gt;            expression);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #0000ff"&gt;throw&lt;/span&gt; &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; LanguageStoreConfigurationException(&lt;span style="color: #006080"&gt;&amp;quot;Token passed to Language Store expression builder was in the wrong format - &amp;quot;&lt;/span&gt; +&lt;br /&gt;                &lt;span style="color: #006080"&gt;&amp;quot;expressions should be in form Language Store Category|Item Title e.g. Search|SearchGoButtonText&amp;quot;&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; sValue = LanguageStore.GetValue(sCategory, sTitle);&lt;br /&gt;&lt;br /&gt;        trace.WriteLineIf(traceSwitch.TraceInfo, TraceLevel.Info, &lt;span style="color: #006080"&gt;&amp;quot;GetEvalData(): Retrieved '{0}' from Language Store.&amp;quot;&lt;/span&gt;,&lt;br /&gt;            sValue);&lt;br /&gt;&lt;br /&gt;        trace.WriteLineIf(traceSwitch.TraceVerbose, TraceLevel.Verbose, &lt;span style="color: #006080"&gt;&amp;quot;GetEvalData(): Returning '{0}'.&amp;quot;&lt;/span&gt;,&lt;br /&gt;            sValue);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; sValue;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;override&lt;/span&gt; &lt;span style="color: #0000ff"&gt;object&lt;/span&gt; EvaluateExpression(&lt;span style="color: #0000ff"&gt;object&lt;/span&gt; target, BoundPropertyEntry entry,&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;object&lt;/span&gt; parsedData, ExpressionBuilderContext context)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; GetEvalData(entry.Expression, target.GetType(), entry.Name);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;override&lt;/span&gt; CodeExpression GetCodeExpression(BoundPropertyEntry entry,&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;object&lt;/span&gt; parsedData, ExpressionBuilderContext context)&lt;br /&gt;    {&lt;br /&gt;        Type type1 = entry.DeclaringType;&lt;br /&gt;        PropertyDescriptor descriptor1 = TypeDescriptor.GetProperties(type1)[entry.PropertyInfo.Name];&lt;br /&gt;        CodeExpression[] expressionArray1 = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CodeExpression[3];&lt;br /&gt;        expressionArray1[0] = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CodePrimitiveExpression(entry.Expression.Trim());&lt;br /&gt;        expressionArray1[1] = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CodeTypeOfExpression(type1);&lt;br /&gt;        expressionArray1[2] = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CodePrimitiveExpression(entry.Name);&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CodeCastExpression(descriptor1.PropertyType, &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; CodeMethodInvokeExpression(&lt;span style="color: #0000ff"&gt;new&lt;/span&gt;&lt;br /&gt;       CodeTypeReferenceExpression(&lt;span style="color: #0000ff"&gt;base&lt;/span&gt;.GetType()), &lt;span style="color: #006080"&gt;&amp;quot;GetEvalData&amp;quot;&lt;/span&gt;, expressionArray1));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;override&lt;/span&gt; &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; SupportsEvaluate&lt;br /&gt;    {&lt;br /&gt;        get { &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff"&gt;true&lt;/span&gt;; }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;If you're wondering why two methods are required, it's because GetCodeExpression() is used where the page has been compiled and EvaluateExpression() is used when it is purely being parsed. My code follows the MSDN pattern which supports both modes and uses a third helper method (GetEvalData()) which both call into. It's this GetEvalData() method which does the work of parsing the passed expression and then using it to obtain the value - in my case the expression is the 'category' and 'title' of the item to fetch from the Language Store. Notice that effectively, the key line in all of that is one line in GetEvalData() which calls my existing LanguageStore.GetValue() method - so effectively my expression builder is just a wrapper for this method. &lt;/p&gt;&lt;p&gt;My web.config entry looks like this:&lt;/p&gt;&lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;&lt;br /&gt;  &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #800000"&gt;add&lt;/span&gt; &lt;span style="color: #ff0000"&gt;expressionPrefix&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;SPLang&amp;quot;&lt;/span&gt; &lt;span style="color: #ff0000"&gt;type&lt;/span&gt;&lt;span style="color: #0000ff"&gt;=&amp;quot;COB.SharePoint.Utilities.LangStoreExpressionBuilder, COB.SharePoint.Utilities.LanguageStore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23afbf06fd91fa64&amp;quot;&lt;/span&gt; &lt;span style="color: #0000ff"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;And finally here's how the component parts of the expression get used:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_DPonmXhJQ4g/SUblxr6y-sI/AAAAAAAAAWs/fJjlyrzySio/s1600-h/ExpressionBuilderSyntax%5B8%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="213" alt="ExpressionBuilderSyntax" src="http://lh3.ggpht.com/_DPonmXhJQ4g/SUblyftPQnI/AAAAAAAAAWw/dWjzXG95Qjk/ExpressionBuilderSyntax_thumb%5B6%5D.jpg?imgmax=800" width="803" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;For SharePoint solutions, assuming we're deploying our code as a Feature/Solution we'd generally want to add the web.config entry automatically by way of the SPWebConfigModification class. You can find the code to do this in on Codeplex in the source code for my &lt;a href="http://www.codeplex.com/SPLanguageStore" target="_blank"&gt;Language Store&lt;/a&gt; solution (in the Feature receiver). &lt;/p&gt;&lt;p&gt;Finally, if you're building an expression builder and this information doesn't get you all the way, the &lt;a href="http://msdn.microsoft.com/en-us/library/system.web.compilation.expressionbuilder.aspx" target="_blank"&gt;MSDN documentation for the ExpressionBuilder&lt;/a&gt; class has some additional details.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Enhancing the design-time experience with a custom expression editor&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;This is something I haven't looked at yet, but looks extremely cool! If the standard expression builder stuff wasn't convenient enough for you, you can extend things further by providing a custom 'ExpressionEditor' for use in Visual Studio. If I understand the possibility correctly, this can provide a better experience in the VS properties grid in two ways:&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;a custom editor sheet (e.g. a dialog to enter the 'category' and 'title' of the Language Store item - let's say 'Search' and 'SearchGoButtonText' was entered respectively, this would 'build' the string in the correct delimited 'Search|SearchGoButtonText' form required)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;a custom picker using the &lt;a href="http://msdn.microsoft.com/en-us/library/system.web.ui.design.htmlcontroldesigner.expressions.aspx" target="_blank"&gt;Expressions&lt;/a&gt; collection - this could (I think) be used to query the Language Store list and display all the items, so that selecting the item to display the translation of is as simple as a few clicks, no typing!&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;p&gt;I'd absolutely love to implement this for the Language Store/Config Store - so I might return to this at a later date!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Conclusion&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Expression builders provide a powerful, clean way to inject method calls into your markup. In most cases we're used to seeing them return strings as in my Language Store/Config Store implementations, and note that the following implementations are also present in the .Net framework:&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.web.compilation.appsettingsexpressionbuilder.aspx"&gt;AppSettingsExpressionBuilder&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.web.compilation.connectionstringsexpressionbuilder.aspx"&gt;ConnectionStringsExpressionBuilder&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.web.compilation.resourceexpressionbuilder.aspx" target="_blank"&gt;ResourceExpressionBuilder&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;p&gt;However, one final thing to bear in mind is that the signature of the method returns an &lt;em&gt;object&lt;/em&gt; - so theoretically it should be possible to do a whole host of other things, where the processsing returns a more complex object which gets assigned to the control property. An example could be data-binding scenarios where your method returns something which implements IEnumerable/IList - this could then be assigned to the DataSource property of your control &lt;em&gt;declaratively&lt;/em&gt;. You might have other possibilities in mind, but hopefully that's food for thought ;-)&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-5836543560406907114?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/8gWIPm1cT_k" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/8gWIPm1cT_k/using-net-expression-builders-to-set.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2008/12/using-net-expression-builders-to-set.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-3929980148946956263</guid><pubDate>Mon, 08 Dec 2008 23:19:00 +0000</pubDate><atom:updated>2009-04-13T19:49:25.169+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">codeplex</category><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><title>Building multi-lingual SharePoint sites - introducing the Language Store</title><description>&lt;p&gt;If you're ever asked to build a multi-lingual site in SharePoint, it quickly becomes apparent that there are a few extra considerations compared to a single language site. These could include:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Information architecture &lt;/li&gt;    &lt;li&gt;Language/culture detection &lt;/li&gt;    &lt;li&gt;Deciding whether to use variations or not &lt;/li&gt;    &lt;li&gt;URL strategy &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;..and so on. Clearly these are decisions which will have a different 'answer' for every multi-lingual project, and typically your client's specific requirements will steer your approach. However one challenge which is likely to remain constant across most such projects is this one:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;How to deal with the many small strings of text &lt;em&gt;which are not part of authored page content&lt;/em&gt; which need to be translated and displayed in the appropriate language &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;This is the challenge I'm focusing on here. To illustrate, here's an example from the BBC site where I've highlighted all the strings which may need to be translated but which don't belong to a particular page:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_DPonmXhJQ4g/STwjlJCOTVI/AAAAAAAAAVU/YNhXxMq_s_0/s1600-h/BBCExample%5B5%5D.jpg"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" alt="BBCExample" src="http://lh4.ggpht.com/_DPonmXhJQ4g/STwjmrgtLBI/AAAAAAAAAVY/eV0_Ofsye9Y/BBCExample_thumb%5B3%5D.jpg?imgmax=800" width="640" height="392" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;..and that's just one page - it turns out a typical site will have &lt;em&gt;many&lt;/em&gt; of these. If you have to translate additional strings shown to authors in edit mode only, you could easily find the total number stretching into the hundreds. So we start to need a framework for storage/retrieval of these 'page furniture' items. If we were dealing with a shrink-wrapped product, .Net resource files could be a good choice, but this approach is probably not flexible enough for a website and won't allow content authors/power users to enter translations. Clearly something based around a SharePoint list is called for, so enter the 'Language Store' - my solution to the problem which you can now download from Codeplex (link at the end).     &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Introducing the Language Store&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;The Language Store is an adaptation of my earlier &lt;a href="http://www.codeplex.com/SPConfigStore" target="_blank"&gt;Config Store&lt;/a&gt; solution and follows some of the same principles:-&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;values are stored in a SharePoint list &lt;/li&gt;    &lt;li&gt;an API is provided to retrieve values with a single method call &lt;/li&gt;    &lt;li&gt;a caching framework is used for optimum performance &lt;/li&gt;    &lt;li&gt;easily deployed as a .wsp &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Items in the list are categorized, and have a column for each language translation:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_DPonmXhJQ4g/ST2xIX9AXII/AAAAAAAAAWk/20RPx2SIrcc/s1600-h/LanguageStoreListNarrow%5B12%5D.jpg"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" alt="LanguageStoreListNarrow" src="http://lh4.ggpht.com/_DPonmXhJQ4g/ST2wutgspfI/AAAAAAAAAWo/hdW75lnFx1s/LanguageStoreListNarrow_thumb%5B10%5D.jpg?imgmax=800" width="719" height="168" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Note that each translation column in the list is named with the convention 'LANG_&lt;em&gt;&amp;lt;culture name&amp;gt;&lt;/em&gt;' (N.B. you might know a 'culture name' as a 'locale ID' or similar) - so when a new language needs to be added to the site, you simply create a new column with the appropriate name and add the translations. A list of culture names can be found in the &lt;a href="http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.aspx" target="_blank"&gt;MSDN documentation for the CultureInfo class&lt;/a&gt;. &lt;/p&gt; &lt;p&gt;&lt;b&gt;UPDATED&lt;/b&gt; - the &lt;a href="http://msdn.microsoft.com/en-us/goglobal/bb896001.aspx"&gt;'National Language Support' API reference page on MSDN&lt;/a&gt; is a better reference - think the CultureInfo page has changed since I linked to it.&lt;/p&gt;   &lt;p&gt;&lt;strong&gt;&lt;u&gt;Retrieving values&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;To retrieve a value, we simply call the GetValue() method and pass the category and title of the item to retrieve:&lt;/p&gt;  &lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;  &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #0000ff"&gt;string&lt;/span&gt; sButtonText = LanguageStore.GetValue(&lt;span style="color: #006080"&gt;&amp;quot;Search&amp;quot;&lt;/span&gt;, &lt;span style="color: #006080"&gt;&amp;quot;SearchGoButtonText&amp;quot;&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;Also, since many of the items we might put in the Language Store are only used in the presentation of the page, it's often a shame to have to switch to the code-behind just to fetch these values and assign them to a control's 'Text' property. So I've provided a tokenized method similar to SPUrl, which allows you to simply drop Language Store values into your markup like this:&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt; &lt;br /&gt;  &lt;pre style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; border-right-style: none; background-color: #f4f4f4; margin: 0em; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px"&gt;&amp;lt;asp:Button runat=&lt;span style="color: #006080"&gt;&amp;quot;server&amp;quot;&lt;/span&gt; id=&lt;span style="color: #006080"&gt;&amp;quot;btnSearch&amp;quot;&lt;/span&gt; Text=&lt;span style="color: #006080"&gt;&amp;quot;&amp;lt;%$ SPLangStore:Search|SearchGoButtonText %&amp;gt;&amp;quot; /&amp;gt;&lt;/span&gt;&lt;/pre&gt;  &lt;br /&gt;&lt;/div&gt;&lt;p&gt;I like this because it means you don't end up cluttering your code-behind with lots of lines just for fetching values from the Language Store and assigning them to ASP.Net labels or controls. For those who don't know how this is done I'll write more about it in the next post as I think it's a cool, under-used facility in .Net. &lt;br /&gt; &lt;br /&gt;&lt;/p&gt;&lt;strong&gt;&lt;u&gt;How the Language Store determines which language to retrieve&lt;/u&gt;&lt;/strong&gt;&lt;p&gt;In the current implementation, the regional settings of the SPWeb are used to determine which translations are retrieved. It's a single method in the code (a single line in fact!), so this scheme could easily be changed if you have a different requirement. We're using the Language Store on our current project, and using the SPWeb setting makes sense for us since we're building around 100 different sites in ~30 languages, as opposed to &lt;em&gt;one&lt;/em&gt; site which displays in the local language (according to the user's thread culture or similar).&lt;/p&gt;&lt;p&gt;Note that if the Language Store doesn't contain a value for the requested culture, a fallback process is used similar to .Net's globalization framework:&lt;/p&gt;&lt;ol&gt;  &lt;li&gt;Check preferred culture&amp;#160; e.g. &lt;b&gt;fr-CH&lt;/b&gt; for French (Switzerland)  &lt;br /&gt;&lt;/li&gt; &lt;li&gt;Check preferred culture's parent e.g. &lt;b&gt;fr&lt;/b&gt; for French     &lt;br /&gt;&lt;/li&gt;  &lt;li&gt;Check default language (determined by configuration) e.g. &lt;b&gt;en&lt;/b&gt; for English  &lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;This is useful where some items might have a different version for say American English (EN-US) and British English (EN-GB), but other items don't require distinction so a single value can be entered into the parent column (EN). &lt;/p&gt;&lt;p&gt;By the way, if you're wondering where the SPWeb regional settings are because you've never needed to change them, they're here:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_DPonmXhJQ4g/ST2rmtlbXTI/AAAAAAAAAVk/_E3hq-NR3qg/s1600-h/RegionalSettingsLink%5B13%5D.jpg"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" alt="RegionalSettingsLink" src="http://lh5.ggpht.com/_DPonmXhJQ4g/ST2rnbMChfI/AAAAAAAAAVo/LcHvPG0JAaA/RegionalSettingsLink_thumb%5B9%5D.jpg?imgmax=800" width="640" height="262" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_DPonmXhJQ4g/STw-kLyXikI/AAAAAAAAAVs/idALr-VrNYg/s1600-h/RegionalSettingsPage%5B5%5D.jpg"&gt;&lt;img style="border-right-width: 0px; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" border="0" alt="RegionalSettingsPage" src="http://lh5.ggpht.com/_DPonmXhJQ4g/STw-lQIrwVI/AAAAAAAAAVw/eXTvtFG45uI/RegionalSettingsPage_thumb%5B3%5D.jpg?imgmax=800" width="640" height="203" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;A checkbox allows the regional settings you make on a given web cascade down to child webs, so we simply set this at the root of the site as a one time operation.&lt;/p&gt;&lt;br /&gt;&lt;strong&gt;&lt;u&gt;Other bits and pieces&lt;/u&gt;&lt;/strong&gt;&lt;br /&gt;&lt;ul&gt;  &lt;li&gt;All items are wrapped up in a Solution/Feature so there is no need to manually create site columns/content types/the Language Store list etc. There is also an install script for you to easily install the Solution.   &lt;br /&gt;&lt;/li&gt;  &lt;li&gt;Caching implementation is currently based around a CacheDependency on a file - this enables the cache on all servers in your farm to be invalidated when an item is updated, but does require that all WFEs can write to this location (e.g. firewalls are not in the way).    &lt;br /&gt;&lt;/li&gt;  &lt;li&gt;The Language Store can also be used where no SPContext is present e.g. a list event receiver. In this scenario, it will look for values in your SharePoint web application's web.config file to establish the URL for the site containing the Language Store (N.B. these web.config keys get automatically added when the Language Store is installed to your site). This also means it can be used outside your SharePoint application, e.g. a console app.   &lt;br /&gt;&lt;/li&gt;  &lt;li&gt;The Language Store can be moved from it's default location of the root web for your site (to do this, create a new list (in whatever child web you want) from the 'Language Store list' template (added during the install), and modify the 'LanguageStoreWebName'/'LanguageStoreListName' keys which were added to your web.config to point to the new location. As an alternative if you already added 100 items which you don't want to recreate, you could use my other tool, the SharePoint Content Deployment Wizard at &lt;a href="http://www.codeplex.com/SPDeploymentWizard"&gt;http://www.codeplex.com/SPDeploymentWizard&lt;/a&gt; to move the list.)    &lt;br /&gt;&lt;/li&gt;  &lt;li&gt;&lt;b&gt;All source code and Solution/Feature files are included&lt;/b&gt;, so if you want to change anything, you can.     &lt;br /&gt;&lt;/li&gt;  &lt;li&gt;Installation instructions are in the readme.txt in the download. &lt;br /&gt;    &lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You can download the Language Store and all source code from &lt;a href="http://www.codeplex.com/SPLanguageStore"&gt;www.codeplex.com/SPLanguageStore&lt;/a&gt;. All feedback welcome!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-3929980148946956263?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/fw38S6S5u9I" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/fw38S6S5u9I/building-multi-lingual-sharepoint-sites.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">29</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2008/12/building-multi-lingual-sharepoint-sites.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-847738557042593965</guid><pubDate>Mon, 24 Nov 2008 07:59:00 +0000</pubDate><atom:updated>2008-11-24T08:00:04.146Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><category domain="http://www.blogger.com/atom/ns#">workflow</category><title>Workflows not being associated with lists/libraries</title><description>&lt;p&gt;I don't often do &amp;quot;a funny thing happened to me on the way to configuring a SharePoint farm recently&amp;quot;-type posts, but I'm making an exception here - this issue not only took a bit of figuring out, it's also kind of interesting! This is partly because SharePoint doesn't do what you'd expect it to do under the circumstances, and you could probably chase your tail for a while on this one if you didn't think it through.&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Scenario:-&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Workflow is not enabled on any of the lists/libraries in the site in production - specifically, there is no association so the workflow list is empty&lt;/li&gt;    &lt;li&gt;The workflow we &lt;em&gt;were&lt;/em&gt; expecting to see enabled is the standard publishing approval workflow - since we are in a WCM/publishing site scenario, this would be associated with each Pages list in the hierarchy&lt;/li&gt;    &lt;li&gt;In our case, initial configuration had just been completed by the hosting company and we were asked to start our testing. Search configuration has not yet been performed&lt;/li&gt;    &lt;li&gt;Previous environments did not display this behaviour&lt;/li&gt;    &lt;li&gt;A custom site definition is used to create &lt;em&gt;any &lt;/em&gt;site/web - this is what determines the approval workflow should be associated&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;At first it seemed like an issue with the site definition. I was thinking that the hosting company had missed a deployment step to STSADM upgradesolution on the .wsp containing the site definition, but accessing the servers showed the latest files in place. I knew that because search configuration hadn't been done yet, the hosting company &lt;em&gt;hadn't yet created the SSP&lt;/em&gt; - this was their next task. I couldn't initially see a link between the SSP and workflow (since SharePoint workflows execute in the w3wp.exe process), but then I started wondering if there was any indirect link, and came up with this as a chain of dependencies:&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="317" alt="image" src="http://lh6.ggpht.com/_DPonmXhJQ4g/SSXj91WNlxI/AAAAAAAAAUc/ygtpiLvhrcI/image%5B11%5D.png?imgmax=800" width="533" border="0" /&gt;&lt;/p&gt;  &lt;p&gt;Could it be that the sites didn't have workflow enabled because the SSP wasn't there at time the sites were provisioned? Sounds plausible, but then I thought hang on - surely what would happen is that workflow associations and so on would be there as usual, but when a workflow form is accessed it would error with the familiar message that session state must be configured:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_DPonmXhJQ4g/SSdcz_7hOwI/AAAAAAAAAUg/GSO1gb0hTvk/s1600-h/InfoPathSessionError%5B11%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="270" alt="InfoPathSessionError" src="http://lh4.ggpht.com/_DPonmXhJQ4g/SSdc0duLjQI/AAAAAAAAAUk/Fqg9CP1lCJc/InfoPathSessionError_thumb%5B7%5D.jpg?imgmax=800" width="755" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Well, as you might have gathered, the answer is no, this isn't what happens! SharePoint genuinely will not (or cannot) add the workflow associations to your lists/libraries, and furthermore they will not magically appear when your SSP is created. You will need to go back and configure workflow on each list/library (or content type) either through the UI or with code.&lt;/p&gt;  &lt;p&gt; So the moral of the story is:&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Create your SSP before any sites are provisioned!&lt;/strong&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-847738557042593965?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/AlQqWvGkj6E" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/AlQqWvGkj6E/workflows-not-being-associated-with.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2008/11/workflows-not-being-associated-with.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-2164709827088407373</guid><pubDate>Tue, 18 Nov 2008 23:38:00 +0000</pubDate><atom:updated>2008-11-18T23:47:50.615Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><category domain="http://www.blogger.com/atom/ns#">Coding</category><title>Simple data access pattern for SharePoint lists</title><description>&lt;p&gt;So, you're in the early stages of your project and coding has started. It's already becoming apparent that some abstraction is needed for accessing data in a few key lists, but you're not sure what. Of course there's no 'one-size-fits-all' answer to this question, but let's run through some options:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Use &lt;a href="http://www.sharepointblogs.com/aghy/archive/2008/10/25/linq4sp-rc2-is-available-to-download.aspx" target="_blank"&gt;LINQ4SP&lt;/a&gt;&amp;#160; &lt;/li&gt;   &lt;li&gt;Use the Repository pattern shown in the recently released &lt;a href="http://www.codeplex.com/spg" target="_blank"&gt;SharePoint Patterns and Practices documentation&lt;/a&gt; &lt;/li&gt;   &lt;li&gt;Build your own Data Access Layer &lt;/li&gt;   &lt;li&gt;Do nothing - allow the calling code to do all the work &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;In my mind the best answer to this question is probably one of the first two - particularly on projects which are building something like a traditional application (i.e. with a domain model and entitities which are tied to data) on top of SharePoint, as opposed to projects which are doing say, simple WCM. However, like me, maybe you've still not spent significant time looking at LINQ4SP (and maybe have memories of the original LinqToSharePoint incarnation not quite making it to maturity) and are either in the same position with the P&amp;amp;P samples or are thinking it looks a little &lt;em&gt;too &lt;/em&gt;enterprise for your current needs. And on a small project with a short dev cycle, I wouldn't blame you. &lt;/p&gt;  &lt;p&gt;So onto other options - in terms of building your own DAL, well of course it's possible, but if you're on a smallish project then presumably finding the time to hand-code an abstraction for all your data needs is going to be tricky. Additionally I'm not too fond of this approach - I've just seen it done poorly by others too many times, resulting in a layer which performs badly/doesn't scale because objects aren't disposed correctly/doesn't provide the convenience it was intended to. And finally, as far as the 'do nothing' option goes, although we might not be striving for the ultimate pattern we &lt;em&gt;are &lt;/em&gt;trying to avoid the duplication, inconsistency and maintenance nightmare that could come if we allow each developer on the project to work with the data how they like.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;My suggestion:-&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;What I've used a couple of times is an 'in between' approach - specifically, in between doing nothing and building a complex DAL. What I'm showing here isn't exactly what I've used (made a couple of 'improvements' as I was tapping it out!), but I think of it as a very quick, lightweight pattern which at least helps reduce the worst of the problems:&lt;/p&gt;  &lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;   &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; Employee&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;readonly&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; ListName = &lt;span style="color: #006080"&gt;&amp;quot;Employees&amp;quot;&lt;/span&gt;;&lt;br /&gt;    &lt;br /&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; Fields&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; PersonTitle = &lt;span style="color: #006080"&gt;&amp;quot;Person_x0020_title&amp;quot;&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; FirstName = &lt;span style="color: #006080"&gt;&amp;quot;First_x0020_name&amp;quot;&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; LastName = &lt;span style="color: #006080"&gt;&amp;quot;Last_x0020_name&amp;quot;&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; StartDate = &lt;span style="color: #006080"&gt;&amp;quot;Start_x0020_date&amp;quot;&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; Division = &lt;span style="color: #006080"&gt;&amp;quot;Division&amp;quot;&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; ID = &lt;span style="color: #006080"&gt;&amp;quot;ID&amp;quot;&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; Salary = &lt;span style="color: #006080"&gt;&amp;quot;Salary&amp;quot;&lt;/span&gt;;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;/// Fetches employee list item - notice that caller has to supply SPWeb object, but all other implementation &lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;/// details (list name, fields names) are taken care of..&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; SPListItem GetEmployeeListItem(&lt;span style="color: #0000ff"&gt;int&lt;/span&gt; EmployeeID, SPWeb Web)&lt;br /&gt;    {&lt;br /&gt;        SPListItemCollection queryResult = executeEmployeeLookup(EmployeeID, Web);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; queryResult[0];&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;/// Fetches employee DataRow for 'read-only' situations, or where we want to cache/serialize etc.&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; DataRow GetEmployeeDataRow(&lt;span style="color: #0000ff"&gt;int&lt;/span&gt; EmployeeID, SPWeb Web)&lt;br /&gt;    {&lt;br /&gt;        SPListItem employeeItem = GetEmployeeListItem(EmployeeID, Web);&lt;br /&gt;        DataRow drEmployee = SPListItemCollectionHelper.CreateDataRowFromListItem(employeeItem);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; drEmployee;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;/// Private method which does actual query.&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; SPListItemCollection executeEmployeeLookup(&lt;span style="color: #0000ff"&gt;int&lt;/span&gt; employeeID, SPWeb web)&lt;br /&gt;    {&lt;br /&gt;        SPList employeeList = web.Lists[Employee.ListName];&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #008000"&gt;// query employee list..&lt;/span&gt;&lt;br /&gt;        SPQuery employeeQuery = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SPQuery();&lt;br /&gt;        employeeQuery.Query = &lt;span style="color: #0000ff"&gt;string&lt;/span&gt;.Format(&lt;span style="color: #006080"&gt;&amp;quot;&amp;lt;Where&amp;gt;&amp;lt;Eq&amp;gt;&amp;lt;FieldRef Name=\&amp;quot;{0}\&amp;quot; /&amp;gt;&amp;lt;Value Type=\&amp;quot;Text\&amp;quot;&amp;gt;{1}&amp;lt;/Value&amp;gt;&amp;lt;/Eq&amp;gt;&amp;lt;/Where&amp;gt;&amp;quot;&lt;/span&gt;,&lt;br /&gt;            Employee.Fields.ID, employeeID);&lt;br /&gt;&lt;br /&gt;        SPListItemCollection employees = employeeList.GetItems(employeeQuery);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; employees;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;/// Example of another method related to employees.&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; SPListItemCollection FetchAllHiresSince(DateTime StartDate, SPWeb web)&lt;br /&gt;    {&lt;br /&gt;        SPList employeeList = web.Lists[Employee.ListName];&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #008000"&gt;// query employee list..&lt;/span&gt;&lt;br /&gt;        SPQuery employeeQuery = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SPQuery();&lt;br /&gt;        employeeQuery.Query = &lt;span style="color: #0000ff"&gt;string&lt;/span&gt;.Format(&lt;span style="color: #006080"&gt;&amp;quot;&amp;lt;Where&amp;gt;&amp;lt;Geq&amp;gt;&amp;lt;FieldRef Name=\&amp;quot;{0}\&amp;quot; /&amp;gt;&amp;lt;Value Type=\&amp;quot;Text\&amp;quot;&amp;gt;{1}&amp;lt;/Value&amp;gt;&amp;lt;/Geq&amp;gt;&amp;lt;/Where&amp;gt;&amp;quot;&lt;/span&gt;,&lt;br /&gt;            Employee.Fields.StartDate, SPUtility.CreateISO8601DateTimeFromSystemDateTime(StartDate));&lt;br /&gt;&lt;br /&gt;        SPListItemCollection employees = employeeList.GetItems(employeeQuery);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; employees;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;Points of note:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;We've centralized the repository details of employee data such as &lt;strong&gt;list name&lt;/strong&gt; and &lt;strong&gt;field names&lt;/strong&gt; &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Getting hold of an Employee list item is now one line for the caller, and they don't need to know how to find the data &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;An SPWeb object must be passed, meaning the caller has responsibility for obtaining and disposing - more on this later &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;The caller has a 'get as DataRow' method - you might feel this isn't needed, but I think it can be a useful API function. In contrast to an SPListItem, a DataRow is completely disconnected from SharePoint and therefore there are no unmanaged SPRequest objects hanging off it which need to be disposed. This means it can be cached/serialized etc., and additionally can be passed around a deep stack of methods without the calling code having to be responsible for disposals (which by the way, would need to be done in a Finally block so the disposals happen even if an exception occurs) &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;For the 'get as DataRow' method, a helper class is used to translate from SPListItemCollection to DataTable and SPListItem to DataRow - this is principally because the existing SPListItemCollection.ToDataTable() method has a bug. (N.B. This is intentionally &lt;em&gt;not&lt;/em&gt; in the form of extension methods for clarity!) &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;The class is not an instance class, so we don't do anything like wrap each list field with a property. This might not be as convenient for the caller, but means we don't have to worry about whether the data item is 'dirty' &lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Update operations are left to the caller &lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;p&gt;This means the calling code (in a SharePoint web context) looks something like this:&lt;/p&gt;&lt;br /&gt;&lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;&lt;br /&gt;  &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; GetReadOnlyEmployee()&lt;br /&gt;{&lt;br /&gt;    DataRow drEmployee = Employee.GetEmployeeDataRow(56, SPContext.Current.Web);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;// do something/pass happily around codebase..&lt;/span&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; GetEmployeeForUpdate()&lt;br /&gt;{&lt;br /&gt;    SPListItem employee = Employee.GetEmployeeListItem(56, SPContext.Current.Web);&lt;br /&gt;    employee[Employee.Fields.Salary] = 50000;&lt;br /&gt;    employee.Update();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;To me, this approach has a good &amp;quot;bang for buck&amp;quot; because it's extremely quick to implement but does make the situation drastically better in team development.&amp;#160; &lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Passing the SPWeb object is key:-&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;In my article &lt;a href="http://www.sharepointnutsandbolts.com/2008/06/disposing-sharepoint-objects-what-they.html" target="_blank"&gt;Disposing SharePoint objects - what they don't tell you&lt;/a&gt;, I highlight the difficulties of keeping track of objects to dispose in a complex class library. However, I'm now starting to see the code I used as an example there as something of an anti-pattern.&amp;#160; The simplified code sample I used to demonstrate the problem was:&lt;/p&gt;&lt;br /&gt;&lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;&lt;br /&gt;  &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; DoSomething()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; bDisposalsRequired = &lt;span style="color: #0000ff"&gt;false&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;// get list from SPContext if we have one..&lt;/span&gt;&lt;br /&gt;    SPList list = getListFromContext();&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (list == &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: #008000"&gt;// otherwise get list from objects we create..&lt;/span&gt;&lt;br /&gt;        list = getInstantiatedList();&lt;br /&gt;        bDisposalsRequired = &lt;span style="color: #0000ff"&gt;true&lt;/span&gt;;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    &lt;span style="color: #008000"&gt;// do something with list..&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;foreach&lt;/span&gt; (SPListItem item &lt;span style="color: #0000ff"&gt;in&lt;/span&gt; list.Items)&lt;br /&gt;    {&lt;br /&gt;        processItem(item);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (bDisposalsRequired)&lt;br /&gt;    {&lt;br /&gt;        list.ParentWeb.Dispose();&lt;br /&gt;        list.ParentWeb.Site.Dispose();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;private&lt;/span&gt; SPList getInstantiatedList()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: #008000"&gt;// can't dispose of these objects here if we're returning a list - we'll be attempting to use &lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #008000"&gt;// objects which have already been disposed of..&lt;/span&gt;&lt;br /&gt;    SPSite site = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; SPSite(&lt;span style="color: #006080"&gt;&amp;quot;http://cob.blog.dev&amp;quot;&lt;/span&gt;);&lt;br /&gt;    SPWeb web = site.OpenWeb(&lt;span style="color: #006080"&gt;&amp;quot;/MyWeb&amp;quot;&lt;/span&gt;);&lt;br /&gt;    SPList list = web.Lists[&lt;span style="color: #006080"&gt;&amp;quot;MyList&amp;quot;&lt;/span&gt;];&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; list;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;private&lt;/span&gt; SPList getListFromContext()&lt;br /&gt;{&lt;br /&gt;    SPContext currentContext = SPContext.Current;&lt;br /&gt;    SPList list = &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (currentContext != &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;)&lt;br /&gt;    {&lt;br /&gt;        list = currentContext.Site.AllWebs[&lt;span style="color: #006080"&gt;&amp;quot;MyWeb&amp;quot;&lt;/span&gt;].Lists[&lt;span style="color: #006080"&gt;&amp;quot;MyList&amp;quot;&lt;/span&gt;];&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; list;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;In this structure, internal API code (i.e. the DoSomething() method) is responsible for obtaining the SPWeb object needed to find the list, which generally means it is also responsible for it's disposal. And this is where the difficulties arise. However, if the &lt;em&gt;caller&lt;/em&gt; has to provide the IDisposable objects, it can then be responsible for calling Dispose() because it knows when they are no longer needed. Most often the caller will simply be passing a reference to SPContext.Current.Web, but if the caller happens to be a Feature receiver then SPFeatureProperties.ListItem.Parent.Web would be passed, or if no existing SPWeb object was available to the caller (e.g. console app) a new SPWeb object would have to be instantiated and passed. Disposals then become much simpler because they can always happen in the same place as any instantiations.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Conclusion:- &lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;I'm sure there are better patterns out there, but even a simple approach like this provides much more structure than leaving it all to the caller (particularly when the calling code is being written by different developers!). For me the key thing is to standardize how code in your codebase accesses list data - much better than having field and list names dotted here, there and everywhere. One thing the approach &lt;em&gt;doesn't&lt;/em&gt; specifically consider is unit-testing - the sample projects in the Patterns &amp;amp; Practices stuff look useful here, and I for one will be getting better-acquainted!&lt;/p&gt;&lt;p&gt;P.S. Thanks to &lt;a href="http://www.thorprojects.com/blog" target="_blank"&gt;Rob Bogue&lt;/a&gt; for helping me crystallize these thoughts, and apologies to &lt;a href="http://sharepoint-sezai-moss-2007.blogspot.com" target="_blank"&gt;Sezai Komur&lt;/a&gt; for not getting round to mailing a draft through earlier as I promised!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-2164709827088407373?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/5GK2A9YJbxI" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/5GK2A9YJbxI/simple-data-access-pattern-for.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2008/11/simple-data-access-pattern-for.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-489575015114912759</guid><pubDate>Tue, 28 Oct 2008 22:30:00 +0000</pubDate><atom:updated>2008-10-28T22:39:20.690Z</atom:updated><category domain="http://www.blogger.com/atom/ns#">WCM</category><title>Sample code from my top WCM tips presentation</title><description>&lt;p&gt;Since &lt;a href="http://www.andrewconnell.com/blog/archive/2008/10/23/Chris-OBriens-Top-5-WCM-Tips.aspx" target="_blank"&gt;AC suggested I post some sample code&lt;/a&gt; for some of the points I was making in my top 5 WCM tips presentation, I've now done just that. In the code you can find examples of the following:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;Exception handler HTTP module&lt;/strong&gt; - sends notification e-mails when an unhandled exception occurs and redirects the user to a configurable 'friendly' error page &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Custom base class for master page&lt;/strong&gt; - this is almost a skeleton class since your implementation will vary, but demonstrates how to use a custom base class &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Trace helper class and matching ReSharper templates&lt;/strong&gt; - for logging what happens in your code (this one's a bonus since I recommend these classes implement trace!)&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;These files can be downloaded in a Visual Studio project from:&lt;/p&gt;  &lt;p&gt;&lt;a title="http://sharepointchris.googlepages.com/wcmsamplecode" href="http://sharepointchris.googlepages.com/wcmsamplecode"&gt;http://sharepointchris.googlepages.com/wcmsamplecode&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;But for those of you who'd just like a quick look at the code, see below.&amp;#160; The actual classes in the project have comments to help you 'use' these things in your site - these have been removed below for readability:&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Exception handler HTTP module code:&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;   &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; System;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; System.Diagnostics;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; System.Text;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; System.Threading;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; System.Web;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; Microsoft.SharePoint;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; Microsoft.SharePoint.Utilities;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; COB.SharePoint.Utilities;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;namespace&lt;/span&gt; COB.Demos.WcmSamples&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; ExceptionHandlerHttpModule : IHttpModule&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: #cc6633"&gt;#region&lt;/span&gt; -- Private members --&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; TraceSwitch traceSwitch = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; TraceSwitch(&lt;span style="color: #006080"&gt;&amp;quot;COB.Demos.WcmSamples.ExceptionHandlerHttpModule&amp;quot;&lt;/span&gt;,&lt;br /&gt;                                                      &lt;span style="color: #006080"&gt;&amp;quot;Trace switch for the COB.Demos.WcmSamples.ExceptionHandlerHttpModule.&amp;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; TraceHelper trace = &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #cc6633"&gt;#endregion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #cc6633"&gt;#region&lt;/span&gt; -- Constructor&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; ExceptionHandlerHttpModule()&lt;br /&gt;        {&lt;br /&gt;            trace = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; TraceHelper(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #cc6633"&gt;#endregion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Init(HttpApplication context)&lt;br /&gt;        {&lt;br /&gt;            context.Error += context_Error;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; context_Error(Object sender, EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            trace.WriteLineIf(traceSwitch.TraceVerbose, TraceLevel.Verbose, &lt;span style="color: #006080"&gt;&amp;quot;context_Error(): Entered error-handling module.&amp;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #008000"&gt;// collect the last error..&lt;/span&gt;&lt;br /&gt;            var exception = HttpContext.Current.Server.GetLastError();&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #008000"&gt;// process it/send e-mail..&lt;/span&gt;&lt;br /&gt;            processError(exception);&lt;br /&gt;&lt;br /&gt;            trace.WriteLineIf(traceSwitch.TraceVerbose, TraceLevel.Verbose, &lt;span style="color: #006080"&gt;&amp;quot;context_Error(): Finished handling error, &amp;quot;&lt;/span&gt; +&lt;br /&gt;                &lt;span style="color: #006080"&gt;&amp;quot;about to redirect.&amp;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #008000"&gt;// and finally clear the error and redirect to the friendly error page..&lt;/span&gt;&lt;br /&gt;            HttpContext.Current.Server.ClearError();&lt;br /&gt;            HttpContext.Current.Response.Clear();&lt;br /&gt;            &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; sErrorUrl = ConfigStore.GetValue(&lt;span style="color: #006080"&gt;&amp;quot;Errors&amp;quot;&lt;/span&gt;, &lt;span style="color: #006080"&gt;&amp;quot;FriendlyErrorPageUrl&amp;quot;&lt;/span&gt;);&lt;br /&gt;            HttpContext.Current.Response.Redirect(sErrorUrl);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; processError(Exception exception)&lt;br /&gt;        {&lt;br /&gt;            trace.WriteLineIf(traceSwitch.TraceVerbose, TraceLevel.Verbose, &lt;span style="color: #006080"&gt;&amp;quot;processError(): Entered.&amp;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; sMessage = buildEmailText(exception);&lt;br /&gt;            trace.WriteLineIf(traceSwitch.TraceInfo, TraceLevel.Info, &lt;span style="color: #006080"&gt;&amp;quot;processError(): Built error string '{0}', calling SendEmail.&amp;quot;&lt;/span&gt;, &lt;br /&gt;                sMessage);&lt;br /&gt;&lt;br /&gt;            sendEmail(sMessage);&lt;br /&gt;&lt;br /&gt;            trace.WriteLineIf(traceSwitch.TraceVerbose, TraceLevel.Verbose, &lt;span style="color: #006080"&gt;&amp;quot;processError(): Leaving.&amp;quot;&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; buildEmailText(Exception exception)&lt;br /&gt;        {&lt;br /&gt;            var messageBuilder = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; StringBuilder();&lt;br /&gt;            messageBuilder.AppendLine(&lt;span style="color: #006080"&gt;&amp;quot;&amp;lt;strong&amp;gt;Date: &amp;lt;/strong&amp;gt;&amp;quot;&lt;/span&gt; + DateTime.Now);&lt;br /&gt;            messageBuilder.AppendLine(&lt;span style="color: #006080"&gt;&amp;quot;&amp;lt;br /&amp;gt;&amp;lt;strong&amp;gt;Message: &amp;lt;/strong&amp;gt;&amp;quot;&lt;/span&gt; + exception.Message);&lt;br /&gt;            messageBuilder.AppendLine(&lt;span style="color: #006080"&gt;&amp;quot;&amp;lt;br /&amp;gt;&amp;lt;strong&amp;gt;Source: &amp;lt;/strong&amp;gt;&amp;quot;&lt;/span&gt; + exception.Source);&lt;br /&gt;            messageBuilder.AppendLine(&lt;span style="color: #006080"&gt;&amp;quot;&amp;lt;br /&amp;gt;&amp;lt;strong&amp;gt;Current user: &amp;lt;/strong&amp;gt;&amp;quot;&lt;/span&gt; + Thread.CurrentPrincipal.Identity.Name);&lt;br /&gt;            messageBuilder.AppendLine(&lt;span style="color: #006080"&gt;&amp;quot;&amp;lt;br /&amp;gt;&amp;lt;strong&amp;gt;Machine name: &amp;lt;/strong&amp;gt;&amp;quot;&lt;/span&gt; + Environment.MachineName);&lt;br /&gt;            messageBuilder.AppendLine(&lt;span style="color: #006080"&gt;&amp;quot;&amp;lt;br /&amp;gt;&amp;lt;strong&amp;gt;Url: &amp;lt;/strong&amp;gt;&amp;quot;&lt;/span&gt; + HttpContext.Current.Request.RawUrl);&lt;br /&gt;            messageBuilder.AppendLine(&lt;span style="color: #006080"&gt;&amp;quot;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;strong&amp;gt;Exception details: &amp;lt;/strong&amp;gt;&amp;quot;&lt;/span&gt; + exception);&lt;br /&gt;            &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; messageBuilder.ToString();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; sendEmail(String message)&lt;br /&gt;        {&lt;br /&gt;            trace.WriteLineIf(traceSwitch.TraceVerbose, TraceLevel.Verbose, &lt;span style="color: #006080"&gt;&amp;quot;sendEmail(): Entered.&amp;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #0000ff"&gt;try&lt;/span&gt;&lt;br /&gt;            {&lt;br /&gt;                var errorToAddress = ConfigStore.GetValue(&lt;span style="color: #006080"&gt;&amp;quot;Errors&amp;quot;&lt;/span&gt;, &lt;span style="color: #006080"&gt;&amp;quot;SendToEmail&amp;quot;&lt;/span&gt;);&lt;br /&gt;                var sErrorSubject = ConfigStore.GetValue(&lt;span style="color: #006080"&gt;&amp;quot;Errors&amp;quot;&lt;/span&gt;, &lt;span style="color: #006080"&gt;&amp;quot;EmailSubject&amp;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;                trace.WriteLineIf(traceSwitch.TraceInfo, TraceLevel.Info,&lt;br /&gt;                    String.Format(&lt;span style="color: #006080"&gt;&amp;quot;SendEmail(): About to send error e-mail to recipient list '{0}' using SPUtility.SendEmail().&amp;quot;&lt;/span&gt;, &lt;br /&gt;                        errorToAddress));&lt;br /&gt;&lt;br /&gt;                SPUtility.SendEmail(SPContext.Current.Web, &lt;span style="color: #0000ff"&gt;false&lt;/span&gt;, &lt;span style="color: #0000ff"&gt;false&lt;/span&gt;, errorToAddress, sErrorSubject, message);&lt;br /&gt;            }&lt;br /&gt;            &lt;span style="color: #0000ff"&gt;catch&lt;/span&gt; (Exception exception)&lt;br /&gt;            {&lt;br /&gt;                var traceMessage = String.Format(&lt;span style="color: #006080"&gt;&amp;quot;Exception thrown attempting to send error e-mail using SPUtility.SendEmail(). &amp;quot;&lt;/span&gt; +&lt;br /&gt;                    &lt;span style="color: #006080"&gt;&amp;quot;Exception Details: {0}&amp;quot;&lt;/span&gt;, exception);&lt;br /&gt;                trace.WriteLineIf(traceSwitch.TraceError, TraceLevel.Error, String.Format(&lt;span style="color: #006080"&gt;&amp;quot;sendEmail(): {0}&amp;quot;&lt;/span&gt;, traceMessage));&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            trace.WriteLineIf(traceSwitch.TraceVerbose, TraceLevel.Verbose, &lt;span style="color: #006080"&gt;&amp;quot;sendEmail(): Leaving.&amp;quot;&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Dispose()&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: #008000"&gt;// nothing to do here..&lt;/span&gt;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;u&gt;Skeleton base class for master page:&lt;/u&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="border-right: gray 1px solid; padding-right: 4px; border-top: gray 1px solid; padding-left: 4px; font-size: 8pt; padding-bottom: 4px; margin: 20px 0px 10px; overflow: auto; border-left: gray 1px solid; width: 97.5%; cursor: text; max-height: 200px; line-height: 12pt; padding-top: 4px; border-bottom: gray 1px solid; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; background-color: #f4f4f4"&gt;&lt;br /&gt;  &lt;pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, &amp;#39;Courier New&amp;#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; System;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; System.Diagnostics;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; System.Web;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; System.Web.UI;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;using&lt;/span&gt; Microsoft.SharePoint;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #0000ff"&gt;namespace&lt;/span&gt; COB.Demos.WcmSamples&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; BasePage : MasterPage&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: #cc6633"&gt;#region&lt;/span&gt; -- Private members --&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; TraceSwitch traceSwitch = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; TraceSwitch(&lt;span style="color: #006080"&gt;&amp;quot;COB.Demos.WcmSamples.BasePage&amp;quot;&lt;/span&gt;,&lt;br /&gt;                                                          &lt;span style="color: #006080"&gt;&amp;quot;Trace switch for the base page class.&amp;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; TraceHelper trace = &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #cc6633"&gt;#endregion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; BasePage()&lt;br /&gt;        {&lt;br /&gt;            trace = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; TraceHelper(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;protected&lt;/span&gt; &lt;span style="color: #0000ff"&gt;override&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; OnLoad(EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            trace.WriteLineIf(traceSwitch.TraceVerbose, TraceLevel.Verbose, &lt;span style="color: #006080"&gt;&amp;quot;OnLoad(): Entered.&amp;quot;&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #008000"&gt;// TODO: add custom code here..&lt;/span&gt;&lt;br /&gt;            &lt;br /&gt;            &lt;span style="color: #0000ff"&gt;base&lt;/span&gt;.OnLoad(e);&lt;br /&gt;&lt;br /&gt;            trace.WriteLineIf(traceSwitch.TraceVerbose, TraceLevel.Verbose, &lt;span style="color: #006080"&gt;&amp;quot;OnLoad(): Leaving.&amp;quot;&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #008000"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #008000"&gt;/// Showing an example base page property..&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #008000"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; UserIsAuthenticated&lt;br /&gt;        {&lt;br /&gt;            get { &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; HttpContext.Current.User.Identity.IsAuthenticated; }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-489575015114912759?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/LpI6ZMWC118" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/LpI6ZMWC118/sample-code-from-my-top-wcm-tips.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2008/10/sample-code-from-my-top-wcm-tips.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-4386636179573179595</guid><pubDate>Wed, 22 Oct 2008 21:12:00 +0000</pubDate><atom:updated>2008-10-22T22:12:30.897+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><category domain="http://www.blogger.com/atom/ns#">WCM</category><title>My top 5 WCM tips presentation</title><description>&lt;p&gt;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:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;I can attempt to convey some of the info which was in my demos here. &lt;/li&gt;    &lt;li&gt;&lt;em&gt;I know&lt;/em&gt; how many of you will read a blog post but not follow a link to a PowerPoint deck! &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;First off, this presentation &lt;em&gt;isn't&lt;/em&gt; 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 &lt;a href="http://www.andrewconnell.com/blog/archive/2008/03/19/WROX-Pro-MOSS-WCM-2007-Book-Its-Finally-Finished-available.aspx" target="_blank"&gt;Andrew Connell's book&lt;/a&gt; (and &lt;a href="http://www.andrewconnell.com/blog" target="_blank"&gt;his blog articles&lt;/a&gt; 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:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Security &lt;/li&gt;    &lt;li&gt;Accessibility &lt;/li&gt;    &lt;li&gt;Optimization &lt;/li&gt;    &lt;li&gt;Deployment &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;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. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Tip #1 - Implement HTML markup in user controls, not page layouts in SPD&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;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.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Slide bullets:&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Faster development experience - less SPD pain [instantaneous save rather than 3-5 second save plus occasional unwanted changes to markup] &lt;/li&gt;    &lt;li&gt;Page layout markup now in primary source control [alongside your other code] &lt;/li&gt;    &lt;li&gt;Much simpler deployment of updates [simply XCOPY user controls when you deploy updates to other environments] &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;What it looks like:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;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):&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/sharepointchris/SPsmRF0KItI/AAAAAAAAATU/wsoIhvpWvHE/s1600-h/PageLayoutMarkup%5B4%5D.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="148" alt="PageLayoutMarkup" src="http://lh4.ggpht.com/sharepointchris/SPsmRyAxA_I/AAAAAAAAATY/8HOMDsaMUQk/PageLayoutMarkup_thumb%5B2%5D.jpg?imgmax=800" width="844" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;..and then all of our real markup code is in the parent user control and child user controls which it references:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/sharepointchris/SPsmSudEGWI/AAAAAAAAATc/L-ezXe6-on0/s1600-h/UserControlMarkup%5B4%5D.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="464" alt="UserControlMarkup" src="http://lh6.ggpht.com/sharepointchris/SPsmUWuB-OI/AAAAAAAAATg/YBh7xeH0mts/UserControlMarkup_thumb%5B2%5D.jpg?imgmax=800" width="992" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Tip #2 - create custom IIS virtual directory pointing to your web project files &lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;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 &lt;em&gt;we avoid having separate design-time and run-time locations for the project files&lt;/em&gt;. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Slide bullets:&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Store user controls/page furniture files (e.g. image/XSL) here. Remove code files (e.g. .cs files) for non-dev environments &lt;/li&gt;    &lt;li&gt;Faster development experience &amp;#8211; no files to copy, no post-build events. Just save and F5! &lt;/li&gt;    &lt;li&gt;Important if using tip #1 &amp;#8211; don&amp;#8217;t want to have to compile project [for post-build event to copy files] just for a HTML change &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;What it looks like:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;In our case we created a virtual directory with an alias of 'MIW' (the name of our website) which points to our project files:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/sharepointchris/SPsmVUXjriI/AAAAAAAAATk/6mDU4TuyEOo/s1600-h/CustomIisVirtualDir%5B4%5D.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="469" alt="CustomIisVirtualDir" src="http://lh5.ggpht.com/sharepointchris/SPsmXfCsbzI/AAAAAAAAATo/tKkYb9p65vk/CustomIisVirtualDir_thumb%5B2%5D.jpg?imgmax=800" width="343" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;All our user control/page furniture file paths then look like '/MIW/PageLayoutsControls/foo.ascx'&amp;#160; etc.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Tip #3 - make life easier for the site authors/admins [reduce their stress and they'll be on &lt;em&gt;your&lt;/em&gt; side]&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;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. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Slide bullets:&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Consider providing custom tools if the &amp;#8216;SharePoint way&amp;#8217; is not simple enough (e.g. user management) &lt;/li&gt;    &lt;li&gt;If you use custom lists for site data, provide a link for authors to find them (e.g. using CustomAction) &lt;/li&gt;    &lt;li&gt;Remember these people are rarely SharePoint gurus! &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;What it looks like:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Clearly this will look different for every project, but for our client we created a custom Site Actions sub-menu with some key links:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/sharepointchris/SPsmYC3R_YI/AAAAAAAAATs/uxC2d2tMAks/s1600-h/CustomSiteActionsOptions%5B4%5D.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="312" alt="CustomSiteActionsOptions" src="http://lh4.ggpht.com/sharepointchris/SPsmYzdRsiI/AAAAAAAAATw/FqxXTAT08OY/CustomSiteActionsOptions_thumb%5B2%5D.jpg?imgmax=800" width="564" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;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:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/sharepointchris/SPsmZ1OdkEI/AAAAAAAAAT0/KpupIWwAM5A/s1600-h/CustomCreateUser%5B4%5D.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="412" alt="CustomCreateUser" src="http://lh3.ggpht.com/sharepointchris/SPsmaroZ16I/AAAAAAAAAT4/d5wM9XJv0dk/CustomCreateUser_thumb%5B2%5D.jpg?imgmax=800" width="765" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;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.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Tip #4 - plan for unexpected errors&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;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 &amp;quot;whaddya mean the website going to go wrong?&amp;quot;, 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. &lt;/p&gt;  &lt;p&gt;There are several tips here, so I'll break them down into 4.1, 4.2 and 4.3:&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;strong&gt;&lt;u&gt;Tip #4.1 - implement 'friendly' pages for 404s and unhandled errors&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;In brief, users of a public-facing website should never see a .Net error if something goes wrong on the website! If this ever &lt;em&gt;does &lt;/em&gt;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? &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Slide bullets:&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Typically use custom HTTP module to override SharePoint's default error-handling behaviour, checking for:      &lt;ul&gt;       &lt;li&gt;HttpContext.Current.Server.GetLastError() &lt;/li&gt;        &lt;li&gt;HttpContext.Current.Response.StatusCode = 404 &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;What it looks like:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;On the Standard Chartered site, our 'friendly' message looks like:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/sharepointchris/SP8I_BeJBuI/AAAAAAAAAT8/cJrdHO6AWM4/s1600-h/CustomErrorScreen%5B4%5D.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="191" alt="CustomErrorScreen" src="http://lh6.ggpht.com/sharepointchris/SP8I_toMTxI/AAAAAAAAAUA/ff9bmDQGI0k/CustomErrorScreen_thumb%5B2%5D.jpg?imgmax=800" width="468" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Tip #4.2 - implement e-mail notifications to developers for errors&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;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 &lt;em&gt;does&lt;/em&gt; 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. &lt;em&gt;However, &lt;/em&gt;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 &lt;em&gt;exactly&lt;/em&gt; 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!&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Slide bullets:&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Means an error cannot happen without the team being aware &lt;/li&gt;    &lt;li&gt;We built this for production, but was even more useful in dev/testing! &lt;/li&gt;    &lt;li&gt;Implemented in same custom HTTP module as error page redirection &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;What it looks like:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/sharepointchris/SP8JAZ0TLwI/AAAAAAAAAUE/mpC2wb8-L3U/s1600-h/ExceptionEmail%5B4%5D.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="474" alt="ExceptionEmail" src="http://lh4.ggpht.com/sharepointchris/SP8JBO-0ckI/AAAAAAAAAUI/P4eqIaGdehU/ExceptionEmail_thumb%5B2%5D.jpg?imgmax=800" width="929" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Tip #4.3 - implement proper tracing in your code&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;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. &lt;em&gt;values&lt;/em&gt; 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 &lt;a href="http://www.jetbrains.com/resharper/" target="_blank"&gt;ReSharper&lt;/a&gt; (e.g. going beyond Visual Studio snippets) to drop in these statements quickly rather than relying on typing or copy/paste.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Slide bullets:&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Provides ability to quickly locate bugs in your code &lt;/li&gt;    &lt;li&gt;Trade off is time/effort to implement &lt;/li&gt;    &lt;li&gt;Consider productivity tools such as ReSharper/CodeRush to lessen impact &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;What it looks like:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;This is showing trace output with &lt;a href="http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx" target="_blank"&gt;DebugView&lt;/a&gt; - notice I've configured DebugView to show me 'warning' trace statements in yellow and 'error' statements in red:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/sharepointchris/SP8JBiol88I/AAAAAAAAAUM/GanuMDfSQxg/s1600-h/TracingError%5B4%5D.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="276" alt="TracingError" src="http://lh3.ggpht.com/sharepointchris/SP8JCR3SdmI/AAAAAAAAAUQ/NIcQMLfqxJI/TracingError_thumb%5B2%5D.jpg?imgmax=800" width="1223" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Tip #5 - design for flexibility&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Again, this one is broken down into two tips:&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Tip #5.1 - using SharePoint lists for configuration data&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;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 &lt;a href="http://www.codeplex.com/SPConfigStore"&gt;www.codeplex.com/SPConfigStore&lt;/a&gt; - this provides the list, content type, caching and API to retrieve 'config items' in code.&amp;#160; 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.&lt;/p&gt;  &lt;p&gt;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. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Slide bullets:&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Use SP lists for values the client may wish to edit, but consider caching      &lt;ul&gt;       &lt;li&gt;My &amp;#8216;Config Store&amp;#8217; solution on Codeplex does this (&lt;a href="http://www.codeplex.com/SPConfigStore"&gt;www.codeplex.com/SPConfigStore&lt;/a&gt;) &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;What it looks like:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/sharepointchris/SP8k4mwTE0I/AAAAAAAAAUU/p2D_V2Gkj30/s1600-h/ConfigStore%5B4%5D.jpg"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="276" alt="ConfigStore" src="http://lh6.ggpht.com/sharepointchris/SP8k53DLTsI/AAAAAAAAAUY/ossktsmvHBs/ConfigStore_thumb%5B2%5D.jpg?imgmax=800" width="1089" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Tip #5.2 - implement a custom master page class&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Although SharePoint gets master pages from .Net 2.0, these really deal with implementing a consistent look and feel only. If you want consistent &lt;em&gt;functionality&lt;/em&gt; or &lt;em&gt;behaviour&lt;/em&gt; 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.&amp;#160;&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Slide bullets:&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Use for any code which should execute on every page load&lt;/li&gt;    &lt;li&gt;Examples on our site:&lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;Check if trial user/when trial access ends&lt;/li&gt;      &lt;li&gt;Check if accepted Terms &amp;amp; Conditions&lt;/li&gt;      &lt;li&gt;Check has supplied their initial user profile info&lt;/li&gt;      &lt;li&gt;Enforce use of HTTPS &lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;Can also use to expose properties for commonly checked items (e.g. username, logged in time)&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;What it looks like:&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&amp;lt;%@ Master language=&amp;quot;C#&amp;quot; Inherits=&amp;quot;MyCompany.MyClient.MyProject.BasePage&amp;quot; %&amp;gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Conclusion/resources&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;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:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;MSDN article: &lt;a href="http://msdn.microsoft.com/en-us/library/cc879144.aspx " target="_blank"&gt;Prescriptive Guidance for WCM sites&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;MSDN article: &lt;a href="http://msdn.microsoft.com/en-us/library/bb727371.aspx" target="_blank"&gt;How to Optimize a SharePoint Server 2007 Web Content Management Site for Performance&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://aks.hisoftware.com" target="_blank"&gt;Accessibility Kit for SharePoint&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-4386636179573179595?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/azdtk9my1pw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/azdtk9my1pw/my-top-5-wcm-tips-presentation.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">17</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2008/10/my-top-5-wcm-tips-presentation.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-2261740112906976054</guid><pubDate>Mon, 06 Oct 2008 21:20:00 +0000</pubDate><atom:updated>2008-10-06T22:22:04.821+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">speaking</category><category domain="http://www.blogger.com/atom/ns#">user group</category><title>Speaking at the UK SharePoint user group on WCM next week</title><description>&lt;p&gt;Next week's UK user group is conveniently being hosted by &lt;a href="http://www.lbi.com/en/London/" target="_blank"&gt;LBi&lt;/a&gt;, my current employer for a Web Content Management (WCM) special. Now I could be biased here, given that the two presenters are me and my current boss, but I think we have some great sessions. In the first, we'll do a case study session partly based on the site I talked about previously in &lt;a href="http://www.sharepointnutsandbolts.com/2008/08/sharepoint-wcm-in-finance-sector.html"&gt;SharePoint WCM in the finance sector&lt;/a&gt; and &lt;a href="http://www.sharepointnutsandbolts.com/2008/08/lessons-learnt-sharepoint-wcm-in.html"&gt;Developer lessons learnt - SharePoint WCM in the finance sector&lt;/a&gt;, and in the second we'll talk about practical steps/best practices to consider when building such sites. &lt;/p&gt;  &lt;p&gt;The date is &lt;strong&gt;Thursday 16th October, location is London (Brick Lane/Liverpool St), and we kick off at 6pm for a 6:30pm start&lt;/strong&gt; - for folks who aren't local or can't make it down, slides etc. will be posted either here or on the &lt;a href="http://suguk.org" target="_blank"&gt;SUGUK&lt;/a&gt; site. The link for full details and registration can be found at the end of this post - the full rundown on the sessions is:&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Session 1 : Web Content Management in MOSS, real life case study &amp;#8211; Riaz Ahmed&lt;/b&gt;    &lt;br /&gt;This session will showcase how SharePoint content management has helped the world&amp;#8217;s most international bank, a top 25 FTSE 100 company, deliver an online digital solution for clients, analysts and employees.    &lt;br /&gt;See how SharePoint has helped empower analysts worldwide to create, publish and share research information. See how SharePoint has helped clients and investors get access to market information to help decide their next multi-million pound deal. See how flexible, extensible and customisable the SharePoint web content management platform can really be.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Session 2 : Best practices/lessons learnt: building MOSS Content Management sites &amp;#8211; Chris O&amp;#8217;Brien     &lt;br /&gt;&lt;/b&gt;    &lt;br /&gt;This presentation explores key tips and developer techniques for building WCM sites, based on the experience of delivering sites such as that shown in the earlier session. As well as key topics such as security, optimisation and deployment, much of the presentation will cover practices which are not widely documented but which can shave time and make the difference on a very tight timescale. And, since the only certainties in life are death, taxes, and clients changing their minds, we&amp;#8217;ll examine how to build a site which is as flexible as possible whilst not being substantially more difficult to implement. Finally, we&amp;#8217;ll wrap up with a discussion on how developers can keep their sanity by being able to fix bugs quickly (especially when the boss is stood over their shoulder!) regardless of where they occur in the code, before going on to open Q &amp;amp; A. &lt;/p&gt;  &lt;p&gt;For full location details and to register see &lt;a title="http://suguk.org/forums/thread/13880.aspx" href="http://suguk.org/forums/thread/13880.aspx"&gt;http://suguk.org/forums/thread/13880.aspx&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;As usual, we'll be going for a pint to continue the chat afterwards - all are welcome!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-2261740112906976054?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/8LXxMist72I" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/8LXxMist72I/speaking-at-uk-sharepoint-user-group-on.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2008/10/speaking-at-uk-sharepoint-user-group-on.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-6587023339238570446</guid><pubDate>Sun, 28 Sep 2008 22:04:00 +0000</pubDate><atom:updated>2008-09-28T23:05:04.039+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">deployment</category><category domain="http://www.blogger.com/atom/ns#">codeplex</category><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><category domain="http://www.blogger.com/atom/ns#">Content Deployment Wizard</category><title>SharePoint dev strategies - it's not all about Features!</title><description>&lt;p&gt;Something I've been meaning to discuss for a long time is the decision to develop SharePoint artifacts using Features or some other approach. I actually discussed this back in May 2007 in my post &lt;a href="http://www.sharepointnutsandbolts.com/2007/05/sharepoint-deployment-optionsfeatures.html" target="_blank"&gt;SharePoint deployment options : Features or Content Deployment?&lt;/a&gt;, but feel it's a topic worth revisiting/expanding on as I often see teams developing with Features without fully working out what &lt;em&gt;exactly&lt;/em&gt; they are getting out of this approach. As you can guess by the article title, I'm not sold on the idea of using Features all the time (readers who have followed this blog from the start might find this surprising given I wrote many &lt;a href="http://sharepointnutsandbolts.blogspot.com/2007/03/serieshow-to-create-common-moss-site.html" target="_blank"&gt;articles on how to work with Features&lt;/a&gt;) and want to put forward some points to consider when working out whether you need them or not. &lt;/p&gt;  &lt;p&gt;Let's first consider some (selected) characteristics of Features:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Provide a means of deploying SharePoint artifacts such as list templates/site columns/content types to multiple environments (e.g dev, test, production) &lt;/li&gt;    &lt;li&gt;Currently the only way to deploy such artifacts across multiple site collections &lt;/li&gt;    &lt;li&gt;Require some extra overhead to create, even with the community tools available (in comparison with creating artifacts directly in the SharePoint UI) &lt;/li&gt;    &lt;li&gt;Little/no support for certain key updates (e.g. updating a content type which has already been deployed and is in use) - updates must be done through the user interface or the API, since &lt;a href="http://msdn.microsoft.com/en-us/library/ms479975.aspx" target="_blank"&gt;modifying the original Feature files to make changes is unsupported&lt;/a&gt;. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Given these points, one scenario I really can't see the benefit of Features for is when the solution consists of just one site collection - which is often the case for WCM sites. Why go through the extra hassle of packaging up artifacts into Features and be faced with difficulties managing updates when the artifacts will only ever exist in one site collection anyway? Sure, they may need to be deployed between &lt;em&gt;environments&lt;/em&gt; but we have other ways of doing that. &lt;/p&gt;  &lt;p&gt;&lt;em&gt;N.B. The same applies to site definitions - why go to the trouble of creating a custom site definition when only one site will ever be created from it?&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;The alternative&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;If you aren't forced into using Features to deal with multiple site collections, &lt;em&gt;not&lt;/em&gt; using them could be the 'most valid' choice. In my recent WCM projects, I haven't used Features for anything which doesn't &lt;em&gt;require &lt;/em&gt;a Feature (e.g. a VS workflow, a CustomAction etc.) for a long time now, including the project I discussed recently in &lt;a href="http://www.sharepointnutsandbolts.com/2008/08/sharepoint-wcm-in-finance-sector.html" target="_blank"&gt;SharePoint WCM in the finance sector&lt;/a&gt; and &lt;a href="http://www.sharepointnutsandbolts.com/2008/08/lessons-learnt-sharepoint-wcm-in.html" target="_blank"&gt;Developer lessons learnt - SharePoint WCM in the finance sector&lt;/a&gt;. Certainly given the extremely tight timescale on that project, I actually feel we could have failed to deliver on time if we had used Features.&lt;/p&gt;  &lt;p&gt;Instead, my approach is to create a blank site in the dev environment, and do all the list/site column/content type/master page development there using the SharePoint UI and SPD. My next step (perhaps not surprising to regular readers) is to use my &lt;a href="http://www.codeplex.com/SPDeploymentWizard" target="_blank"&gt;Content Deployment Wizard&lt;/a&gt; tool to move all the SharePoint artifacts to the other environments when ready. Equally, you could choose to write your own code which does the same thing using the now well-documented &lt;a href="http://blogs.technet.com/stefan_gossner/archive/2007/08/30/deep-dive-into-the-sharepoint-content-deployment-and-migration-api-part-1.aspx" target="_blank"&gt;Content Deployment API&lt;/a&gt;. You'll need to deal with any filesystem and .Net assets separately (generally &lt;em&gt;before&lt;/em&gt; you import the SharePoint content on the destination), but in my view we've at least drastically simplified the SharePoint side of things. This seems to work well for many reasons:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;More efficient since no development time lost to building Features &lt;/li&gt;    &lt;li&gt;The update problem described earlier is taken care of for you (by the underlying Content Deployment API) - as an example, add a field to a content type in dev, deploy the content which uses it and the field will be added on the import site &lt;/li&gt;    &lt;li&gt;Concept of a 'package' is maintained, so .cmp files produced by the Wizard can be handed to a hosting company for them to import using the Wizard at their end. I hear of quite a few people doing this. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;We can store the .cmp files in source control and use them as part of a 'Software Development Lifecycle' approach&lt;/strong&gt;. My approach (and I'd guess that of others using the tool in this way) is to store the .cmp file alongside the filesytem files such as .ascx files for the current 'release', and import them as part of the deployment process of moving the release to the next environment. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;As an aside, when I decided to write a tool which would simplify dealing with dev/QA/UAT/production environments on SharePoint projects, I was initially torn between 'solving the content type update problem' and something based around the Content Deployment API. One reason why I decided on the latter was because the CD API already seemed to have solved the other issue! &lt;/p&gt;  &lt;p&gt;Now I'm certainly not saying it works perfectly every time (it doesn't, though is much improved following SP1 and infrastructure update), but in my experience I seem to spend less time over the course of a project resolving deployment issues than I would do building/troubleshooting Features. Additionally, using Content Deployment allows deployment of, well, content - if your solution relies on pre-created publishing pages or you have a scenario such as your client creating some content in UAT which needs to be moved to production before go-live, Features won't help you here. The Content Deployment mechanism however, is designed for just that.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Where do Solutions (.wsp) fit in all this?&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;So to summarize the above, my rule of thumb for projects which aren't built around multiple site collections is &lt;em&gt;don't use Features for things which don't absolutely require them&lt;/em&gt;. So where does that leave Solution packages (.wsp files) - should they be abandoned too? Well no, definitely not in my view. Solutions solve a slightly different problem set:-&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Deploying files to SharePoint web servers such that each server in a farm is a mirror of another. Ensuring all web front-ends have the same files used by SharePoint is, of course, a key requirement for SharePoint farms - this applies to Feature files when using them, but also to assemblies, 12 hive files etc.&lt;/li&gt;    &lt;li&gt;Web.config modifications e.g. the &amp;#8216;SafeControls&amp;#8217; entry required for custom web parts/controls &lt;/li&gt;    &lt;li&gt;Code Access Security config modifications e.g. those required for controls not running from the GAC&lt;/li&gt;    &lt;li&gt;Some other tasks, such as deployment of web part definition files (.webpart) &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Really, there's nothing stopping you from doing all this manually if you wanted to (especially if you're always deploying to a single server, so there are less things to keep in sync). But the point here is that Solutions genuinely do make your life easier for comparatively little effort, so the 'cost/benefit' ratio is perhaps different to Features for me - the key is using one of the automated build approaches such as &lt;a href="http://www.codeplex.com/wspbuilder" target="_blank"&gt;WSP Builder&lt;/a&gt;. So, my recommendation would generally be to always use Solutions for assemblies, 12 hive files etc., particularly in multiple server farm environments.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Conclusion&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;My rules of thumb then, are:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Consider &lt;strong&gt;not&lt;/strong&gt; using Features (and site definitions) if your site isn't based around multiple site collections - using the &lt;a href="http://www.codeplex.com/SPDeploymentWizard" target="_blank"&gt;Wizard&lt;/a&gt; or some other solution based on Content Deployment can be the alternative&lt;/li&gt;    &lt;li&gt;Use Solutions if you have multiple servers/environments, unless you're happy to have more work to do to keep them in sync&lt;/li&gt;    &lt;li&gt;If you &lt;strong&gt;are &lt;/strong&gt;using Features, plan an approach for dealing with updates such as content type updates&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;My message here possibly goes against some of the guidance you might see other folks recommend, but I'm just going on the experience I've had delivering projects using different approaches. As always, the key is to consider deployment approach &lt;em&gt;before &lt;/em&gt;you actually come to do it!&lt;/p&gt;  &lt;p&gt;P.S. Also remember, &lt;a href="http://blogs.technet.com/stefan_gossner/archive/2008/03/12/common-error-situation-with-when-using-backup-restore-to-transfer-a-database-to-a-new-farm-on-moss-2007.aspx" target="_blank"&gt;deploying using backup and restore is a bad idea&lt;/a&gt; ;-)&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-6587023339238570446?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/ZZkZNZ6RHwU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/ZZkZNZ6RHwU/sharepoint-dev-strategies-it-not-all.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">13</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2008/09/sharepoint-dev-strategies-it-not-all.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-3748927802833950679</guid><pubDate>Mon, 22 Sep 2008 00:41:00 +0000</pubDate><atom:updated>2008-09-22T01:49:04.976+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">codeplex</category><category domain="http://www.blogger.com/atom/ns#">Content Deployment Wizard</category><category domain="http://www.blogger.com/atom/ns#">content deployment</category><title>Source code for SP Content Deployment Wizard now released!</title><description>&lt;p&gt;Back from holiday now, and hopefully with something useful for those of you who use my &lt;a href="http://www.codeplex.com/SPDeploymentWizard" target="_blank"&gt;Content Deployment Wizard&lt;/a&gt; tool to move SharePoint content around. I've now released the source code for the tool onto Codeplex, so if you have an interest in the Content Deployment API or just want to see how the wizard works, it's now all there for you. This is kind of a big step for me as I saw the tool more as a &amp;quot;free product&amp;quot; (built over many late nights after Suzanne was asleep!) rather than an open source project - I figured I &lt;em&gt;would&lt;/em&gt; release the code at some point, but intended to wait a little longer until the next release, since I'm actually halfway through changing the code over for the new functionality. However, my hand was slightly forced because the Codeplex administrators suspended the project due to me not having supplied source code - apparently this is a rule of the site which I had missed. I wasn't happy with the tool not being available for people who wanted it, so wanted to rectify this as soon as possible. So this explains why anybody attempting to download the tool over the last 3 or 4 days was unable to - sincerely apologize for the inconvenience people!&lt;/p&gt;  &lt;p&gt;In any case, it had been on my mind because there seems to be an increase recently in people building their own tools and functionality around the &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.deployment.aspx" target="_blank"&gt;Content Deployment API&lt;/a&gt; - it's certainly an area with a lot of scope for custom solutions, and folks occasionally drop me a line with a request for a help. So it seems right to stop being precious about it and release the code sooner rather than later. I also fixed an annoying bug which caused an unhandled exception if a user with insufficient SharePoint permissions used the tool.&lt;/p&gt;  &lt;p&gt;Anyway, hope it's useful to someone!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.codeplex.com/SPDeploymentWizard" target="_blank"&gt;Content Deployment Wizard - Codeplex site homepage&lt;/a&gt;    &lt;br /&gt;&lt;a href="http://www.codeplex.com/SPDeploymentWizard/Release/ProjectReleases.aspx?ReleaseId=17537" target="_blank"&gt;Content Deployment Wizard - Release 1.1 with source code&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;P.S. I have a couple of things temporarily ahead of Wizard development in the queue, but as mentioned previously the focus for next version will be to add support for command-line use/scripting. This means the Wizard can be used in scheduled/automated deployments, with the benefit of the &lt;em&gt;granular&lt;/em&gt; selection of content to deploy which standard Content Deployment does not provide. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-3748927802833950679?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/KN1w37KYbvE" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/KN1w37KYbvE/source-code-for-sp-content-deployment.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">3</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2008/09/source-code-for-sp-content-deployment.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-2070949193766476825</guid><pubDate>Sun, 31 Aug 2008 21:49:00 +0000</pubDate><atom:updated>2008-09-01T08:50:56.112+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><category domain="http://www.blogger.com/atom/ns#">WCM</category><title>Developer lessons learnt - SharePoint WCM in the finance sector</title><description>&lt;p&gt;So in my recent &lt;a href="http://www.sharepointnutsandbolts.com/2008/08/sharepoint-wcm-in-finance-sector.html" target="_blank"&gt;SharePoint WCM in the finance sector&lt;/a&gt; post, I talked about what we built and why I think the result is kind of interesting. What I want to do today is share some of the technical lessons learnt, and give a sense of what worked and what didn't. As I mentioned last time, UK-based folks will hopefully be able to gain more than I can provide here when the site gets presented at the &lt;a href="http://suguk.org/" target="_blank"&gt;SharePoint UK user group&lt;/a&gt;, meaning we'll answer any question you care to come up with, not just some of the developer stuff I want to discuss today.&lt;/p&gt;  &lt;p&gt;Now to frame all this, it's important to consider the type of project this was - the terms mean slightly different things to different people, but to me the emphasis was on 'development', as opposed to 'implementation' or 'customization'. In code terms, we ended up with the following:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;17 Visual Studio projects in total &lt;/li&gt;    &lt;li&gt;4 Windows services &lt;/li&gt;    &lt;li&gt;5 nightly batch processes &lt;/li&gt;    &lt;li&gt;5 supplementary SQL tables (outside of the SharePoint db) &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Not bad for 8 weeks work. As an aside, although the first number seems surprisingly high, in the technical washup I did with the team nobody thought this &lt;em&gt;wasn't &lt;/em&gt;the right way to factor the code. This is partly explained by the fact this single project is actually part of a bigger program of projects being done for the client, and also partly by the complexity of the Endeca search implementation and batch processes we needed.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;What worked well (in no particular order)&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;Using a 'development farm' amongst the developers &lt;/strong&gt;- this means the content database is shared, and thus no effort is required for one developer to see the lists, site columns, content types, master pages/layouts etc. created by others. This is actually the &lt;strong&gt;only&lt;/strong&gt; way to do team development in SharePoint for me, but worthwhile mentioning it as I know not all SharePoint shops do things this way. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Proper use of tracing -&lt;/strong&gt; this is the idea of writing log statements throughout code to easily diagnose problems once the code has been deployed to other environments (e.g. QA/UAT/production). We used the standard .Net &lt;a href="http://msdn.microsoft.com/en-us/library/system.diagnostics.trace.aspx" target="_blank"&gt;System.Diagnostics trace framework&lt;/a&gt; with levels of Verbose, Info, Warning and Error - this has been familiar to me for a long time but a couple of the devs were new to it and agreed it was invaluable. In particular, we had a lot of library code and it's often difficult to find logic bugs when you can't directly see the result of something on a screen. For me, tracing essentially gives you the power to find certain bugs in seconds or minutes which could otherwise take hours to resolve. Although adding the tracing code can slow down coding, to mitigate this we used.. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;&lt;a href="http://www.jetbrains.com/resharper/" target="_blank"&gt;ReSharper&lt;/a&gt; -&lt;/strong&gt; at the start of the project I created several ReSharper templates to call our common code e.g. for tracing, and got all the team to download trial versions of ReSharper. This meant we could add trace statements in just a few keystrokes, meaning the 'I didn't have time to add trace!' excuse couldn't be used :-) &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;My &lt;a href="http://www.codeplex.com/SPConfigStore" target="_blank"&gt;Config Store framework&lt;/a&gt; based on a SharePoint list * -&lt;/strong&gt; we stored over 130 'configuration items', from 'True'/'False' config switches such as 'enforce password change for users first logon,' to known URLs, to certain strings displayed throughout the site. We also found a couple of areas for improvement (e.g. field not big enough to store XML fragments!) which will hopefully make it into the next release. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Implementing logging/notifications for unhandled exceptions -&lt;/strong&gt; I know the MS Enterprise Library a component for this, but we developed our own using a HTTP handler which sits in front of SharePoint's SPRequest handler. This means that whenever something happens in the code which we're not expecting, we get to find out about it immediately and can see the stack trace and other debug info in the e-mail. This was invaluable when the testers got to work, as it meant we could proactively deal with bugs before they even got reported. As soon as we noticed a mail with a new exception, we shout over to the particular test guy (identified by the user ID) &amp;quot;What exactly did you &lt;em&gt;just&lt;/em&gt; do?&amp;quot; (which impressed them greatly!), so we nail the exact set of circumstances/data which caused the bug right there and then.&amp;#160; &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;My &lt;a href="http://www.codeplex.com/SPDeploymentWizard" target="_blank"&gt;Content Deployment Wizard&lt;/a&gt; tool * -&lt;/strong&gt; I also played the 'deployment/release manager' role on this project, so I was probably the guy who benefited from this the most but I've actually used it somewhere on every single project I've done since building it. For releases when the team had updated x page layouts, x lookup lists and x Config Store items, the tool is invaluable for picking out &lt;em&gt;just &lt;/em&gt;the changed items and deploying them to the other environments. For Config Store items in particular it was useful as some config is different between environments (similar to web.config keys) so you don't want to overwrite the entire list. For early releases when the team had made lots of of complex 'schema' updates (such as lots of intricate changes to site columns/lookups/content types), due to the time pressures I elected to take the 'everything will definitely work this way' route and drop the site collection on the target and import the whole thing (since no valuable data to preserve) so there are some complex deployment scenarios I still haven't fully tested personally, but with 3 environments on top of the dev environment to deploy to the Wizard was prettes tilitiy much a lifesaver. &lt;/li&gt;    &lt;li&gt;&lt;a href="http://tonybierman.blogspot.com/2008/07/free-custom-cross-site-lookup-column.html" target="_blank"&gt;&lt;strong&gt;Cross-site lookup field by SharePoint Solutions&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; -&lt;/strong&gt; this solves the problem that a lookup column can only lookup data in the current web. We use this for several key sets of data so we get to have one copy and one copy only. Damn useful. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb386976.aspx" target="_blank"&gt;LINQ to SQL&lt;/a&gt; -&lt;/strong&gt; we use this for CRUD operations in our supplementary SQL tables, and the guys who used it agreed they saved significant time over the standard approach of writing ADO.Net code. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;* hopefully it doesn't come across as shameless self-promotion to include these - the very reason I built them was to solve recurring problems I saw on SharePoint dev projects, and both utilities really did help us here.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Project challenges (in no particular order)&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;Team Foundation Server weirdness -&lt;/strong&gt; for reasons we still haven't established, we found the .csproj file for the web project (i.e. the most critical VS project!) would be checked out whenever a developer compiled the solution. With multiple checkout enabled, this means that pretty much every developer had the project file checked out all the time, regardless of whether he/she was making any project changes (e.g. adding new classes). This meant we had many more merge issues than normal - not fun. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;VM issues -&lt;/strong&gt; for a while we thought ReSharper was the culprit here, but a VS hotfix brought more stability. A hunch says at least some of the issues are 64-bit related (our dev environment was matched to production in this respect), since often the problem would manifest itself via Visual Studio (a 32-bit application remember). Frequent VS crashes, &amp;quot;attempted to read or write protected memory&amp;quot; messages in the event log - oh joy. &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Failure to identify shared code soon enough&lt;/strong&gt; - often a concern on complex development projects when the team is working at high speed. We did daily standup meetings (similar to scrum) but I suspect we may have focused too much on &lt;em&gt;issues&lt;/em&gt; rather than what &lt;em&gt;was&lt;/em&gt; being 'successfully' developed. So we lost some time to &lt;a href="http://en.wikipedia.org/wiki/Refactoring" target="_blank"&gt;refactoring&lt;/a&gt; to bring things back in line, but this is why I like to think of the approach as '&lt;strong&gt;Dangerously&lt;/strong&gt; Rapid Application Development' (for those who remember the term ;-))&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Issues arising from sharing IP addresses on SSL -&lt;/strong&gt; in several of our environments, we attempted to use the technique documented by Adrian Spear in &lt;a href="http://mcmsfaq.com/cs2/blogs/adrian_spear/archive/2007/03/07/205.aspx" target="_blank"&gt;To setup SSL on multiple Sharepoint 2007 web applications using host headers under IIS 6.0&lt;/a&gt;. I've used this successfully in the past but had some problems this time round - despite working fine in our QA environment, we had problems in other places. After carefully analyzing the differences, I worked out that this technique will &lt;em&gt;only&lt;/em&gt; work if the SSL certificate being used is a wildcard certificate or is matched on the machine name rather than the site URL. This might be obvious to other people but wasn't to me!&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Hope someone finds this useful!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-2070949193766476825?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/gTQ88D09XDg" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/gTQ88D09XDg/lessons-learnt-sharepoint-wcm-in.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2008/08/lessons-learnt-sharepoint-wcm-in.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4126985520350746834.post-1544600429019839663</guid><pubDate>Wed, 13 Aug 2008 22:54:00 +0000</pubDate><atom:updated>2008-08-14T00:03:09.379+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SharePoint</category><category domain="http://www.blogger.com/atom/ns#">WCM</category><title>SharePoint WCM in the finance sector</title><description>&lt;p&gt;You might have been wondering where I've been for the last few weeks, so I thought I'd show rather than just tell. I rarely talk about specific 'daytime' projects I work on, but I guess I'm quite proud of this one and you might be interested in what we've done. I've been working with &lt;a href="http://www.lbi.com/en/London/" target="_blank"&gt;LBi&lt;/a&gt; (Europe's biggest design/technology agency) recently, leading a team of 5-7 developers on a WCM (Web Content Management) project for one of the global investment banks. You can see some aspects of what we've done with SharePoint in this post, and I'll follow up with some developer-to-developer info about techniques we used in the next one. Additionally, for UK-based folks I think &lt;a href="http://www.linkedin.com/in/riazahmed" target="_blank"&gt;Riaz Ahmed&lt;/a&gt; (my current boss and Mr SharePoint at LBi) is hoping to do a &lt;em&gt;real&lt;/em&gt; show and tell at the &lt;a href="http://suguk.org" target="_blank"&gt;UK SharePoint user group&lt;/a&gt; in a couple of months, where there'll be the opportunity for Q&amp;amp;A and to pick up far more info/lessons learnt (both technical and non-technical) than I can provide here.&lt;/p&gt;  &lt;p&gt;The site is targeted at a closed audience over the internet (our client's clients), so unfortunately you can't see it for yourself - the site provides analysts with high-value information on financial markets, primarily in the form of market reports and newsflashes, though there are other content types. The user experience we were tasked with implementing (created by LBi's design team) means the user gets to have some pretty impressive functionality around all this (IMHO) which differentiates the service from others.&lt;/p&gt;  &lt;p&gt;The centrepiece is the market report data, which is held for many countries and topics:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/sharepointchris/SKNmTpdBmrI/AAAAAAAAANA/8bRC3leQlq0/s1600-h/MarketReport_b%5B4%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="480" alt="MarketReport_b" src="http://lh4.ggpht.com/sharepointchris/SKNmUf7LDnI/AAAAAAAAANE/TV6CEQjzkR8/MarketReport_b_thumb%5B2%5D.jpg?imgmax=800" width="545" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;This is imported nightly from a master datasource (thus lending itself nicely to caching), and our client can also add comments to the imported data via SharePoint WCM page editing. Perhaps the slickest piece of functionality is the facility for the user to create personalized 'smart reports' with specific markets and topics which interest him/her. These are saved against the user's profile for quick access:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/sharepointchris/SKNmVDl0rmI/AAAAAAAAANI/-ZjyV7MTJSU/s1600-h/ConfigureSmartReportSmall_b%5B4%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="424" alt="ConfigureSmartReportSmall_b" src="http://lh5.ggpht.com/sharepointchris/SKNmVzpu9VI/AAAAAAAAANM/fkpZei4f4Vo/ConfigureSmartReportSmall_b_thumb%5B2%5D.jpg?imgmax=800" width="640" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;At runtime we build the personalized page from the same cache layer used by the standard pages in the first shot.&lt;/p&gt;  &lt;p&gt;Since financial markets change quickly, our client's authoring teams (distributed around the world), can quickly create newsflashes via SharePoint publishing functionality for display on the site - this is controlled via SharePoint workflow. Users can search and filter by country, date, high priority flag and can also store in a 'My newsflashes' bucket for quick retrieval:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/sharepointchris/SKNmWnvWFCI/AAAAAAAAANQ/ppMSVii7mdg/s1600-h/NewsflashesSmall_b%5B4%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="480" alt="NewsflashesSmall_b" src="http://lh4.ggpht.com/sharepointchris/SKNmXoiCSkI/AAAAAAAAANU/t1wNKUIlPuM/NewsflashesSmall_b_thumb%5B2%5D.jpg?imgmax=800" width="556" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Users also have the option of receiving e-mail alerts when newsflashes are added to the site (immediate for high priority newsflashes, nightly batch for others), and can tailor their e-mail preferences by country...&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/sharepointchris/SKNmYkbGEQI/AAAAAAAAANY/r-nttDswrMw/s1600-h/MyAccountCountryPreferencesSmall_b%5B4%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="480" alt="MyAccountCountryPreferencesSmall_b" src="http://lh5.ggpht.com/sharepointchris/SKNmZafkwBI/AAAAAAAAANc/ll09YSUDyyQ/MyAccountCountryPreferencesSmall_b_thumb%5B2%5D.jpg?imgmax=800" width="602" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;..and topic:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/sharepointchris/SKNmZy9pLqI/AAAAAAAAANg/tMNU5sD_Ev8/s1600-h/MyAccountMailPreferencesSmall_b%5B4%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="449" alt="MyAccountMailPreferencesSmall_b" src="http://lh6.ggpht.com/sharepointchris/SKNmanpoxKI/AAAAAAAAANk/lLY5qeseMgw/MyAccountMailPreferencesSmall_b_thumb%5B2%5D.jpg?imgmax=800" width="640" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The site also has a ton of other good stuff, such as:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;single sign-on with a sister site &lt;/li&gt;    &lt;li&gt;integration with a higher-level portal which can authenticate users itself (meaning users do not log-in separately on our site) &lt;/li&gt;    &lt;li&gt;WCAG 'A' -level accessibility - accessibility wasn't particularly a focus of the client for this site, but (happily) is just how things are done here anyhow. We use a lot of JavaScript to enhance the user experience but it &lt;em&gt;always &lt;/em&gt;provides equivalent functionality when disabled - I have to say the LBi front-end developers use some incredibly innovative techniques to achieve this, I've rarely seen anything like it (see below for some examples). &lt;/li&gt;    &lt;li&gt;integration with &lt;a href="http://endeca.com/" target="_blank"&gt;Endeca&lt;/a&gt; for search (including the newsflash landing page you see above - this is powered by search) &lt;/li&gt;    &lt;li&gt;custom admin functionality for user creation/management &lt;/li&gt;    &lt;li&gt;many many smaller but 'interesting' requirements such as newsflashes having user-selected 'related items', attachments etc etc! &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Rich, accessible controls&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;In addition to all the personalization, one of the things that I love about the user experience is the funky controls we have. So why have a dull dropdown like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/sharepointchris/SKNmbfRMBsI/AAAAAAAAANo/m_6QgQ_fP1A/s1600-h/StandardDropdown%5B5%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="305" alt="StandardDropdown" src="http://lh3.ggpht.com/sharepointchris/SKNmbx8Uy1I/AAAAAAAAANs/OakYidvciUc/StandardDropdown_thumb%5B3%5D.jpg?imgmax=800" width="177" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;...when you can have one like this: &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/sharepointchris/SKNmcczQr5I/AAAAAAAAANw/WrdZ6-nOniw/s1600-h/CountryControlExpandedMid%5B3%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="185" alt="CountryControlExpandedMid" src="http://lh3.ggpht.com/sharepointchris/SKNmc8jEtRI/AAAAAAAAAN0/jSKYstPUHxs/CountryControlExpandedMid_thumb%5B1%5D.jpg?imgmax=800" width="214" border="0" /&gt;&lt;/a&gt;&amp;#160; &lt;/p&gt;  &lt;p&gt;This allows us to present only countries the user has &lt;em&gt;told&lt;/em&gt; us they are interested in in the dropdown (thus avoiding clutter), whilst allowing them to easily see data for &lt;em&gt;other&lt;/em&gt; countries if they have the occasional need to. To do this, clicking the 'More...' link expands the dropdown to show a second section containing the other countries (or more specifically, the other countries they are permitted to see):&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/sharepointchris/SKNmdbwP_NI/AAAAAAAAAN4/KnBq-EGJDLk/s1600-h/CountryControlExpandedFull%5B4%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="471" alt="CountryControlExpandedFull" src="http://lh5.ggpht.com/sharepointchris/SKNmd0ay6pI/AAAAAAAAAN8/iJ9l3PksWzA/CountryControlExpandedFull_thumb%5B2%5D.jpg?imgmax=800" width="207" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Similarly for &lt;em&gt;multiple&lt;/em&gt; selections, why have this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/sharepointchris/SKNmeQK0r2I/AAAAAAAAAOA/zioDNQ-6-8k/s1600-h/StandardListbox%5B4%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="149" alt="StandardListbox" src="http://lh3.ggpht.com/sharepointchris/SKNmewT4wqI/AAAAAAAAAOE/BTUnmByk_Ps/StandardListbox_thumb%5B2%5D.jpg?imgmax=800" width="418" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;...when you can have this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/sharepointchris/SKNmfccyEvI/AAAAAAAAAOI/T2QTqN6W1UM/s1600-h/CountryControlMultiExpandedMid%5B3%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="189" alt="CountryControlMultiExpandedMid" src="http://lh5.ggpht.com/sharepointchris/SKNmfycWZFI/AAAAAAAAAOM/poB-fBFXxlM/CountryControlMultiExpandedMid_thumb%5B1%5D.jpg?imgmax=800" width="210" border="0" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Again, clicking the 'More...' link allows the user to select a country not in their 'preferred' countries:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/sharepointchris/SKNmgVsobKI/AAAAAAAAAOQ/ezUCpZ65EAs/s1600-h/CountryControlMultiExpandedFull%5B4%5D.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="281" alt="CountryControlMultiExpandedFull" src="http://lh4.ggpht.com/sharepointchris/SKNmg4qVlAI/AAAAAAAAAOU/ZlF5SJ8KW7Y/CountryControlMultiExpandedFull_thumb%5B2%5D.jpg?imgmax=800" width="204" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Personally I think this makes for a great user experience, and it's all accessibility-compliant.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Project plan : caffeine&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;The development time for everything you see here (and everything you don't) was around 7-8 weeks, though this excludes some of the front-end development (HTML, CSS, JS). We're not quite over the finishing line yet, but are in the final stages of user acceptance testing and security testing. To say it's been tough would be something of an understatement, but I'm incredibly proud of my guys and it's been a great team effort - fortunately for me they're very talented :-)&lt;/p&gt;  &lt;p&gt;Next time I'll detail some of the technical decisions we took and go through some developer bits which we felt worked well when building/deploying the site.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4126985520350746834-1544600429019839663?l=www.sharepointnutsandbolts.com'/&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ChrisObrien/~4/3zI2m20UC7c" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/ChrisObrien/~3/3zI2m20UC7c/sharepoint-wcm-in-finance-sector.html</link><author>noreply@blogger.com (Chris O'Brien)</author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">8</thr:total><feedburner:origLink>http://www.sharepointnutsandbolts.com/2008/08/sharepoint-wcm-in-finance-sector.html</feedburner:origLink></item></channel></rss>
