My goal here was convert my Community Server 2007 installation to have a single blog (and have a URL of the format http://domain.tld/blog) and maintain my file library (at http://domain.tld/files). Since I switched to SmugMug, I have photo galleries disabled in addition to turning off forums, FeedReader, and BlogRoller. To accomplish the transition to a single-blog, I added a communityserver_override.config, siteurls_override.config, and then added some URL rewriting so that all the old links across other blogs and in search engines do not break.
My communityserver_override.config file looks like this:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <Overrides>
3: <Override xpath="/CommunityServer/Tasks/Threads/Thread/task[@name='ForumsIndexing']" mode="remove" />
4: <Override xpath="/CommunityServer/Tasks/Threads/Thread/task[@name='GalleryIndexing']" mode="remove" />
5: <Override xpath="/CommunityServer/Tasks/Threads/Thread/task[@name='FilesIndexing']" mode="remove" />
6: <Override xpath="/CommunityServer/Tasks/Threads/Thread/task[@name='FeedUpdater']" mode="remove" />
7: <Override xpath="/CommunityServer/Tasks/Threads/Thread/task[@name='RollerBlogsUpdater']" mode="remove" />
8: <Override xpath="/CommunityServer/Tasks/Threads/Thread/task[@name='ImportPhotoJob']" mode="remove" />
9: <Override xpath="/CommunityServer/Tasks/Threads/Thread/task[@name='UserInvitationExpiration']" mode="remove" />
10: <Override xpath="/CommunityServer/Tasks/Threads/Thread/task[@name='ReportingVisitors']" mode="remove" />
11: <Override xpath="/CommunityServer/Tasks/Threads/Thread/task[@name='ReportingPageViews']" mode="remove" />
12: <Override xpath="/CommunityServer/Tasks/Threads/Thread/task[@name='RebuildThumbnailsJob']" mode="remove" />
13:
14: <Override xpath="/CommunityServer/Tasks/Threads/Thread[@minutes='5']" mode="update">
15: <Thread minutes="1">
16: <task name="Emails" type="CommunityServer.MailRoom.Components.EmailJob, CommunityServer.MailGateway.MailRoom" enabled="true" enableShutDown="false" failureInterval="1" numberOfTries="10" />
17: </Thread>
18: </Override>
19:
20: <Override xpath="/CommunityServer/Weblog" mode="change" name="blogFileStorageLocation" value="~/images" />
21: <Override xpath="/CommunityServer/Weblog" mode="new" name="defaultApplicationKey" value="nino" />
22:
23: <Override xpath="/CommunityServer/CSModules/add[@name='RollerBlogRules']" mode="remove" />
24: <Override xpath="/CommunityServer/CSModules/add[@name='WeblogPostandArticleHtmlScrubbing']" mode="remove" />
25: <Override xpath="/CommunityServer/CSModules/add[@name='AutoBlogCreate']" mode="remove" />
26: <Override xpath="/CommunityServer/CSModules/add[@name='AutoApproveForumModule']" mode="remove" />
27: <Override xpath="/CommunityServer/CSModules/add[@name='BBcodeToHtml']" mode="remove" />
28: <Override xpath="/CommunityServer/CSModules/add[@name='IrcCommands']" mode="remove" />
29: <Override xpath="/CommunityServer/CSModules/add[@name='ForumCensorship']" mode="remove" />
30: <Override xpath="/CommunityServer/CSModules/add[@name='ForumEmoticon']" mode="remove" />
31: <Override xpath="/CommunityServer/CSModules/add[@name='ForumSourceCode']" mode="remove" />
32: <Override xpath="/CommunityServer/CSModules/add[@name='HtmlNestingCorrectionModule']" mode="remove" />
33: </Overrides>
Of particular note here is the setting of the defaultApplicationKey value (line 21). You will need to set this to your blog app key (You can view the application key for your blog in Control Panel > Administration > Blogs > Blogs. It is the value of the "Name" column for the given blog.).
My siteurls_override.config looks like this:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <Overrides>
3: <Override xpath="/SiteUrls/transformers/add[@key='##blogdirectory##']" mode="change" name="value" value="" />
4: <Override xpath="/SiteUrls/locations/location[@name='weblogs']" mode="change" name="path" value="/blog/" />
5: <Override xpath="/SiteUrls/locations/location[@name='weblogs']" mode="new" name="physicalPath" value="/blogs/" />
6: <Override xpath="/SiteUrls/locations/location[@name='weblogs']" mode="change" name="type" value="CommunityServer.Blogs.Components.SingleBlogLocation, CommunityServer.Blogs" />
7: </Overrides>
I would like to point out that in the original guidance from Ken, he said that the ##blogdirectory## transform value (line 3) should be "/"; however, this resulted in odd-looking URLs of the sort http://nino.net/blog//archive/2007/04.aspx It is certainly legitimate, but the "//" bothered me, so I changed the value to "" and it works just fine. Note that you need to restart the app pool (via a direct recycle or by touching the web.config) in order for CS to pick up the _override.config files.
Now, I have my blog in single-blog 'mode', and I my blog URL is now http://nino.net/blog . Swell. Unfortuantely this now breaks links search engines and from other blogs. Time for some URL rewriting. Can't I just do this via the URL rewriting in CS? Not quite.
I settled on using the UrlRewriter.Net HttpModule. Configuring this HttpModule required a little RegEx action. I turned to the excellent Expresso regex development tool to help me validate my regex. To get UrlRewriter.net up and running I had to, generically, do two things: drop the .dll in my /bin and edit my web.config. My web config edits are as follows:
I added a <configSections> node. Remember, this section, if it exists, must be the first section under the <configuration> node.
<configSections> <section name="urlrewritingnet" restartOnExternalChanges="true" requirePermission ="false" type="UrlRewritingNet.Configuration.UrlRewriteSection, UrlRewritingNet.UrlRewriter" /> </configSections>
I then located the <httpModules> section and added the UrlRewriteModule entry like so:
<httpModules> <add name="UrlRewriteModule" type="UrlRewritingNet.Web.UrlRewriteModule, UrlRewritingNet.UrlRewriter" />
(remainder of <httpModules> section omitted for brevity)
Lastly, I added the <urlrewritingnet> section (this needs to be outside of <system.web>, but inside <configuration>):
1: <urlrewritingnet
2: rewriteOnlyVirtualUrls="true"
3: defaultPage="default.aspx"
4: xmlns="http://www.urlrewriting.net/schemas/config/2006/07" >
5: <rewrites>
6: <add name="Blog"
7: virtualUrl="~/blogs/nino/default.aspx"
8: destinationUrl="~/blog/default.aspx"
9: redirect="Application"
10: redirectMode="Permanent"
11: rewriteUrlParameter="ExcludeFromClientQueryString"
12: ignoreCase="true" />
13: <add name="FullyQualifiedBlogEntry"
14: virtualUrl="~/blogs/nino/archive/(20\d\d)/(0[1-9]|1[012])/(0[1-9]|[12][0-9]|3[01])/(.*).aspx"
15: destinationUrl="~/blog/archive/$1/$2/$3/$4.aspx"
16: redirect="Application"
17: redirectMode="Permanent"
18: rewriteUrlParameter="ExcludeFromClientQueryString"
19: ignoreCase="true" />
20: <add name="FullyQualifiedBlogEntryComment"
21: virtualUrl="~/blogs/nino/archive/(20\d\d)/(0[1-9]|1[012])/(0[1-9]|[12][0-9]|3[01])/(.*).aspx#comments"
22: destinationUrl="~/blog/archive/$1/$2/$3/$4.aspx#comments"
23: redirect="Application"
24: redirectMode="Permanent"
25: rewriteUrlParameter="ExcludeFromClientQueryString"
26: ignoreCase="true" />
27: <add name="MonthlyArchive"
28: virtualUrl="~/blogs/nino/archive/(20\d\d)/(0[1-9]|1[012]).aspx"
29: destinationUrl="~/blog/archive/$1/$2.aspx"
30: redirect="Application"
31: redirectMode="Permanent"
32: rewriteUrlParameter="ExcludeFromClientQueryString"
33: ignoreCase="true" />
34: <add name="Tags"
35: virtualUrl="~/blogs/nino/archive/tags/(.*)/default.aspx"
36: destinationUrl="~/blog/archive/tags/$1/default.aspx"
37: redirect="Application"
38: redirectMode="Permanent"
39: rewriteUrlParameter="ExcludeFromClientQueryString"
40: ignoreCase="true" />
41: <add name="AboutMe"
42: virtualUrl="~/blogs/nino/pages/about-me.aspx"
43: destinationUrl="~/blog/pages/about-me.aspx"
44: redirect="Application"
45: redirectMode="Permanent"
46: rewriteUrlParameter="ExcludeFromClientQueryString"
47: ignoreCase="true" />
48: <add name="Syndication"
49: virtualUrl="http\://nino.net/blogs/nino/(rss|atom).aspx"
50: destinationUrl="http://feeds.feedburner.com/NinoBenvenuti"
51: redirect="Domain"
52: redirectMode="Permanent"
53: rewriteUrlParameter="ExcludeFromClientQueryString"
54: ignoreCase="true" />
55: </rewrites>
56: </urlrewritingnet>
In these seven entries, I have covered the base blog URL, a fully-qualified blog entry, blog entry comments, monthly archives, tags, the "About Me" page, and my syndication URLs. The regex is, IMO, straightforward. I suggest the excellent http://www.regular-expressions.info/ for further study on regular expressions. I would like to note that for the regex on lines 14 and 21, I could have used virtualUrl="~/blogs/archive/(.*)/(.*)/(.*)/(.*).aspx" or virtualUrl="~/blogs/archive/20[0-9][0-9]/[0-1][0-9]/[0-3][0-9]/(.*).aspx", but my final solution is more efficient (definitely more efficient than using the dot "(.*)"). Also note that I have the redirectMode set to "Permanent"; this will send the browser a HTTP 301 response, telling it that this change is permanent (as opposed to setting it to "Temporary" which would result in the browser receiving a HTTP 302).
Since I was so pleased with the UrlRewriting.net HttpModule and wanted to thank the authors in a way other than linking to them, I gave them a small donation. It felt good, too.