September 1, 2010 - 15:30, by Dostalek, Kevin
I just spent a bunch of time working around a SharePoint bug related to manipulating the order that navigation items appear in a MOSS Publishing site on the TopNavigationBar. Here I will spell out what you need to get to get various pieces of this to work- but first, let's define what we are trying to accomplish.
Say you have a site that has been created in some way. It doesn't really matter if it was declaritively deployed from a Site Definition, restored from a backup, generated by a SharePoint Deployment Wizard CMP, or built up manually from code. The thing is once it's done being created you need to modify from code various aspects of the top navigation bar including manually setting the order of the items. Again, it doesn't matter where this code is running from, whether it be a PowerShell script, a command line application, or a suicide webpart placed on the site. Ok, all that being said, *MY* specific requirements were to:
- Change Top (global) Nav to Include Subwebs, Hide Pages, and support manual (non-automatic) ordering
- Hide one of the subwebs
- Reorder the other subwebs to a custom (ie. arbitrary) order
- All changes must be done fully in code with no "manual human steps" required as it was part of an automated deployment script.
The good news is that to do the first two bullets, things pretty much work like they are supposed to, although finding exactly how to do some of that took a bit of work, so I'll reproduce them here. Unfortunately for the third bullet there's a bug that gets in the way and so weird workarounds are required.
First of all, my code segments below will assume you have referenced Microsoft.SharePoint.dll and Microsoft.SharePoint.Publishing.dll and have the following usings at the top of your file:
1: using Microsoft.SharePoint;
2: using Microsoft.SharePoint.Publishing;
3: using Microsoft.SharePoint.Navigation;
4: using System.Collections.Generic;
To set the different properties of the root web's navigation settings simply do the following:
1: using (var site = new SPSite(webUrl))
2: {
3: var rootPubweb = PublishingWeb.GetPublishingWeb(site.RootWeb);
4: rootPubweb.NavigationOrderingMethod = OrderingMethod.Manual;
5: rootPubweb.IncludeSubSitesInNavigation = true;
6: rootPubweb.IncludePagesInNavigation = false;
7: rootPubweb.Update();
8: }
Now, if you want to hide a particular subweb from the top navigation, you actually need to set the property of the subweb, not a property in the root web or it's navigation collections. So, you would add the following (inside the SPSite using statement):
1: var subwebs = rootPubweb.GetPublishingWebs();
2: subwebs["subwebToHide"].IncludeInGlobalNavigation = false;
3: subwebs["subwebToHide"].Update();
4: rootPubweb.Update();
So far so good. Now when it gets to actually specifying the order of the nodes, the documentation that you will find will direct you to get an SPNavigationNodeCollection from rootWebInstance.Navigation.TopNavigationBar. If you iterate over the SPNavigationNode 's in this collection you can set their order by using the .MoveToFirst, .MoveToLast, and .Move methods. Unfortunately on a newly build publishing site this collection will not contain any of your subwebs, only the top level pages (even though we specified to NOT include pages and to include subwebs). In scouring the internet I found that others had indeed come across this bug as well. What's odd is that if you use the SharePoint GUI and go to the root web's navigation settings (/_layouts/AreaNavigationSettings.aspx), select any subweb in the "Global Navigation" tree and click one of the Move buttons, then some magic code runs behind the scenes and the Navigation.TopNavigationBar collection will now be properly populated (and you won't need the following work around any more for that particular site). However, as my requirement was to not require any manual steps I had to go on to develop the workaround below.
So I embarked on an effort to reproduce the "magic code" that initialized the navigation collection properly. There was much trial and error involved while making incremental changes and then interogating object properties with PowerShell, etc... In the end the successful strategy was to create the navigation nodes manually (in code). The trickiest part though was getting them to be set as "internal" and not "external" links (this is a read-only property on each NavigationNode)-- it turned out the node's Url property had to be set just right for it to work. Here is my workaround code:
1: using (var site = new SPSite(webUrl))
2: {
3: var rootPubweb = PublishingWeb.GetPublishingWeb(site.RootWeb);
4: var subwebs = rootPubweb.GetPublishingWebs();
5:
6: //Build a temporary hash of the node titles (so we can use them instead of index numbers)
7: SPNavigationNodeCollection nodes = site.RootWeb.Navigation.TopNavigationBar;
8: Dictionary<string, SPNavigationNode> nodeHash = new Dictionary<string, SPNavigationNode>();
9: for (int x = 0; x < nodes.Count; x++)
10: {
11: nodeHash.Add(nodes[x].Title, nodes[x]);
12: }
13:
14: //This block fixes the evil SharePoint defect ------------------------
15: foreach (PublishingWeb subweb in subwebs)
16: {
17: if (!nodeHash.ContainsKey(subweb.Title))
18: {
19: string relUrl = subweb.ParentPublishingWeb.Uri.MakeRelativeUri(subweb.Uri).ToString();
20: SPNavigationNode nn =
21: Microsoft.SharePoint.Publishing.Navigation.SPNavigationSiteMapNode.CreateSPNavigationNode(
22: subweb.Title, relUrl, NodeTypes.Area, site.RootWeb.Navigation.TopNavigationBar);
23: nn.Update();
24: }
25: }
26: site.RootWeb.Update();
27: //---------------------------------------------------------------------
28:
29: //Rebuild the hash table since now we should have the correct nodes!
30: nodeHash = new Dictionary<string, SPNavigationNode>();
31: for (int x = 0; x < nodes.Count; x++)
32: {
33: nodeHash.Add(nodes[x].Title, nodes[x]);
34: }
35:
36: string[] subwebTitlesInOrder = {"FirstNavItem", "SecondNavItem", "ThirdNavItem", "FourthNavItem"};
37:
38: //Order the nodes in the proper order
39: SPNavigationNode currentNode;
40: SPNavigationNode previousNode=null;
41:
42: for (int i = 0; i < subwebTitlesInOrder.Length; i++)
43: {
44: currentNode = nodeHash[subwebTitlesInOrder[i]];
45: if (i == 0)
46: {
47: currentNode.MoveToFirst(nodes);
48: }
49: else
50: {
51: currentNode.Move(nodes, previousNode);
52: }
53: previousNode = currentNode;
54: }
55: site.RootWeb.Update();
56: }
And that's it!
August 7, 2010 - 15:00, by Dostalek, Kevin
Wrapped up another DevLink today with my session this morning: Building a SharePoint 2010 Development Environment. It was a full room and a great audience. Thanks very much to all who made it in for the Saturday morning session (I know how rough that can be after Friday night shenanigans :)
Anyway, I've posted below a hires picture version of the mindmap used in the presentation as well as a PDF version of the basic installation walkthrough. Below that is a list of all the places we talked about on the internet so that you have all the links in once place. I'd love to hear any additional feedback from the folks that attended (since at least at the moment they appear to have misplaced the speaker evals from this morning-- I'm sure they will turn up though).
Click one of the two pictures below to download the presentation resources:


Links:
July 24, 2010 - 20:00, by Dostalek, Kevin


So for those of you following me on Twitter or Facebook, you might know that this blog is now running on SharePoint Foundation 2010 and the newly released Community Kit for SharePoint Extended Blog Edition Version 3.0. It was rumored that this may work, but also a little bit cryptic in the release notes, only saying:
*Preliminary SharePoint 2010 Beta 3 compatible (with web.config edits)
No mention what these web.config edits may be however, and when I tried reaching out to some of the folks on the dev team I didn't actually get an answer, but I did hear from Matthew McDermot that it did indeed work, and so I decided to just give it a go and deal with the problems as they came up. So here goes, but first, if you need to download the software, do so with the following links:
Now I had an existing site running in it's own web application (and it's own content database) on WSS 3.0 and CKS:EBE 2.0 (never upgraded production to 2.5). So my first step was to get that upgraded to SPF2010. Since I had it in it's own separate content DB this was pretty straight forward. I just detached and reattached the DB in the SQL servers, created a new web application on SPF, and then ran the following STSADM command:
stsadm -o addcontentdb -url http://thekickboard.com -databasename WSS_Content_TKB -preserveolduserexperience false
A couple notes about this upgrade process:
- You have to run that command from stsadm or powershell. The GUI doesn't support adding content databases that require upgrading.
- You need to make sure if you are using all the default database settings (server, username, password, etc...) like I did above that you give the appropriate accounts access to your newly attached content DB
- You can use other methods for doing this upgrade of course (in place, etc..) If you want to do the DB attach method but don't have the site in it's own content DB, you can do a site collection backup with stsadm, then restore it to a new web application (with it's own content DB). Don't try restoring your backup directly to the SPF server though, that will fail since there's no "upgrader" in that process (that would have been a nice feature though). For more information on how to migrate to SharePoint 2010 see the TechNet Resource Center on the topic.
Next we need to install CKS:EBE 3.0 on the server. You might be thinking that would have been a good idea to do before, but I tried that and ran into some troubles. It probably doesn't matter, but I'm sure it works this way. So anyway, on your server deploy the CKSEBE.wsp file. Make sure that you activate both the web application scope feature as well as the web scope feature. If you navigate to the site, you will notice some things working (like all your previous settings should have migrated over into the new CKS:EBE settings page in Site Settings), but a lot that isn't working, like your theme.
First of all, it's not necessarily your theme or the MTF that isn't working, but rather the HTTP module that does the URL redirection needs some attention. This, I gather is the "with web.config edits" the note above was referring to. So crack open web.config on the site and find the following line:
<add name="CksEbeModule" type="CKS.EBE.BlogHttpModule, CKS.EBE, Version=0.1.0.0, Culture=neutral, PublicKeyToken=3e8b700c069fb747" />
You will find the above in the <httpModules> section. Go ahead and CUT that line above (we'll be pasting it somewhere else in a minute). Next find the section simply labeled <modules>. In this section you will probably find 5 <remove> tags and 4 <add> tags. Paste the "add" tag that you cut above so that it's after all the removes, but before the add's in the modules section. Also, you need to add a preCondition="integratedMode" attribute to this element. So, your whole <modules> section will look something like this (with your additions bolded below):
<modules runAllManagedModulesForAllRequests="true">
<remove name="AnonymousIdentification" />
<remove name="FileAuthorization" />
<remove name="Profile" />
<remove name="WebDAVModule" />
<remove name="Session" />
<add name="CksEbeModule" preCondition="integratedMode" type="CKS.EBE.BlogHttpModule, CKS.EBE, Version=0.1.0.0, Culture=neutral, PublicKeyToken=3e8b700c069fb747" />
<add name="SPRequestModule" preCondition="integratedMode" type="Microsoft.SharePoint.ApplicationRuntime.SPRequestModule, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
<add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<add name="SharePoint14Module" preCondition="integratedMode" />
<add name="RSRedirectModule" type="Microsoft.ReportingServices.SharePoint.Soap.RSRedirectModule, RSSharePointSoapProxy, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" />
</modules>
[EDIT: 7/25/2010] I also discovered that for some things to work like the RSS feed and other items controlled by httpHandlers (like pingbacks if you are crazy enough to enable those) need to be moved from the <httpHandlers> section of web.config to the <handlers> section. Just cut and copy them as you did above, it would also be good to give each element a name attribute as well. [/EDIT]
Ok, so now at this point you can hopefully pull up the EBE site and have the MTF working. If not, then try going into the CKSEBE settings and change your default theme to DEFAULT (and make sure you have that one deployed). This will eliminate any issues in your customized theme for a baseline. Another gotcha (which got me) is that you need to make sure that you have all of your access mappings done correctly. It seems like under SPF the BlogHttpModule is a lot more finicky that it was under WSS3. In my case I have my blog server being reverse proxied by an IIS 7.5 server, so it can get a little confusing with all the host headers flying around.
So now we are at a point that the OOB CKS themes should be working on SPF 2010. Yey! The next step is to get your theme working. If you made any changes to the "content" pages, like home.aspx, post.aspx, categories.aspx, and month.aspx, then now is the time to go and make those again since when you activated the new CKS:EBE web feature it would have overwritten those that were upgraded from your old content DB. For example, in mine, since I don't use webparts on my blog, I changed all of these pages to not inherit from webpart page since that does lots of crazy things like pushing down core.css and core/init.js which we don't need. I also don't have a placeholder for blogcomments in my theme's masterpage since I use JS-KIT Echo. If I didn't remove those from the content pages, then they would throw errors.
And finally, the one last thing that I had to tweak to get my blog working again was related to the tagcloud.xsl where it was rendering all of my tags tiny (w8 class). After a little debugging it appeared that the PostCount variable was not getting set for each category that came back in the <EBE:CategoriesList> webcontrol. A quick look in reflector showed me that this control has a property IncludePostCount. It seems that in the past this defaulted to true and now it defaults to false. Not a big deal, just make sure you set it to true in the <EBE:CategoriesList> webcontrol if you want those populated so that the TagCloud.xsl can do it's thing and calculate relative frequency styling.
I have a feeling I will find other little quirks along the way- which won't be a suprise since this version doesn't actually make claims that it is "SP2010 Ready" or anything. I may start trying to take advantage of other SP2010 native functions over time, but for now if nothing else, I recommend this upgrade for the sole reason that the post editing interface is so much nicer!
If you want to read more about how this blog is put together and more about customizing the CKS:EBE (older 2.0 version), then look through the kickboard tag on this blog.
June 14, 2010 - 13:56, by Dostalek, Kevin
Seems like I keep getting behind on posting these (at least every other one-- then I catch up by posting two :) Anyway a few weeks back I did a presentation here in Indianapolis for the
Indy TechFest. I had a great audience both in size, over 50, and in makeup-- almost everyone there was a developer, which is a first for me when giving this DEV presentation. This time around I did my "Leveraging SharePoint 2010 as a Social Computing Development Platform" session, and it was the first time that I presented it on the RTM bits (and it all worked! yey!).
As usual, I didn't do slides, only a mindmap, which can be
downloaded here. If you'd like the sample code, read my blog posts from
SPS Charlotte and
nSpin as they have a link and some pointers as how to get it all to work.
Thanks to the event organizers, sponsors, volunteers, and attendees (especially all of you that came to my session!) for a great day!
May 19, 2010 - 12:20, by Dostalek, Kevin
This past weekend I had the privilege of participating in the largest SharePoint Saturday event to date! The event took place in Annandale, VA which is just south of our nation's capital (hence the event name - SPSDC). There were over 900 attendees and 90 speakers not to mention numerous sponsors, volunteers, and of course organizers. The event was pulled off very smoothly with no real noticeable mishaps which for this size of event is quite impressive especially when you consider that it was not planned out by folks that do event planning for a living! Kudos to the organizing committee and all of the volunteers that kept all of the logistical issues at bay!
My session was in the last slot of the day, and I presenting my 75 minute version of "Building a Kickin' Public Facing Blog with the CKS:EBE". While I don't particularly like this time slot, you get what you get, and really this is probably a good end of the day session as it's mostly fun and doesn't require too much brain stretching. With 13 other high quality sessions going on as well, my audience was a bit smaller than normal, but they were very much engaged in the content, so I couldn't ask for more! For those that attended, looking for the mindmap, you can grab it by clicking on the thumbnail below. I've also sent it over to the SPSDC Site to be posted with all the other session slide decks as well. Don't forget to also browse the kickboard tag on this blog site for more in depth screencast versions on the same topic but more focused on specific design and development tasks.

As I've come to expect at these events delivering a presentation (sharing my art) and attending others presentations (learning and accepting the gift of their art) are quite sufficient reasons to participate. However, what truly makes it worth driving (or in this case flying) across the country, being away from the family, etc... is the interactions I get to have with the community outside of the seminar rooms. Because of the sheer size of this event there are way too many of these to call out here-- I did truly enjoy meeting a ton of new folks and catching up with a lot of old friends. There were a few special moments though that stand out though (funny that most of them involve food or drink):
- Lunch on Friday with Dan Usher, Scott Singleton, and Mark Rackley
- Sitting between Michael Lotter and Becky Isserman (Ma and Pa SPS) and meeting about 30 new people at the speaker dinner
- Hanging out with Eric Harlan, Mark Miller, Ruven Gotz, Cathy Dew, and a bunch of others Friday night
- Getting logo design advice from Cathy Dew and Marcy Kellar while they psychoanalyzed my speech patterns
- A huge SharePint (at least 50-60 people at peak I think) and having dinner with SandyU, Janis Hall and Dan Lewis
- Chatting late into the night at the after-SharePint SharePint with Brian Jackett, Dan Usher (how were you still awake!), Geoff Varosky, Cathy Dew, Ruven Gotz, Monica Rosenberg, Joy Earles, Janis Hall, Dan Lewis and bout 10 others
- And finally, folks that I seemed to run into all weekend long and just fire up conversations with: Fabian Williams, Christina Wheeler, and Christian Buckley
So that about wraps it up.. special thanks to Dux, Jennifer, Dan, and Gino for pulling off an awesome event. Hope to see lots of you all again at other events this year. Next stops for me will be Indy Tech Fest and then SPS Ozarks!