Wednesday, August 29, 2007

Manage Publishing Page Nodes in Site Navigation

Hi all,
What I am publishing here is some undocumented issues
involving navigation items in publishing enabled sites.

When you have publishing feature enabled in MOSS sites publishing
pages from “pages” library automatically set to be displayed in navigation. So far
– nothing is new.

What you didn’t know is that when you try to access these
items in the site’s navigation in order to perform a simple task such as hiding
a page from navigation, changing a page’s display order and other tasks
you are in to a nasty surprise!

What you want to do is to:

A: Get an instance of the current web site (SPWeb web = SPControl.GetContextWeb(Context) from within a web part.

B: Get the left navigation nodes collection (using web.Navigation.QuickLaunch)

C: Loop over navigation items and move/hide/rename etc… (foreach(SPNavigationNode node in web.Navigation.QuickLaunch))

But wait –

If you just created the site and added some pages to it you
are in to a nasty surprise as I promised. You will notice that
none of your
pages
that are displayed in the navigation are in that collection!

Actually the collection should be empty unless you added
some other items to it manually.

You will also notice a very weird behavior of
SharePoint that as soon as you open the “Navigation” page in the UI of
that site and make any change and save it – since that change all items
suddenly
appear in the navigation collection
as you expected them to from the
beginning!

My customer and I had to create pages and sites by code and
change their ordering by the same code. Of course we didn’t plan on browsing
each site and making a change manually – so we had to find a solution for that
issue.

The following code fixes that “problem”. It goes over a publishing
site pages library and make sure that all pages navigation nodes were created
and available for change using your code.

This took me quite a lot of time to investigate and find
this solution since it is not documented anywhere!!! So enjoy…

SPWeb curWeb = GetWeb();
PublishingWeb pWeb = PublishingWeb.GetPublishingWeb(curWeb);
//Get existing links in navigation
//(so we know if to create new or move existing link)
List<string> existing = new List<string>();
foreach(SPNavigationNode node in curWeb.Navigation.QuickLaunch)
existing.Add(node.Url.ToLower());
//Loop for each page in pages library
foreach(SPListItem item in pWeb.PagesList.Items)
{
//Get a fresh copy of web – curWeb =
//new SPSite(curWeb.Site.ID).OpenWeb(curWeb.ID);...
curWeb = Utilities.Refresh(curWeb);

PublishingPage pp = PublishingPage.GetPublishingPage(item);
SPNavigationNode nn = null;
string ppUrl = pp.Uri.AbsolutePath.ToLower();
//check if exists in navigation
if (existing.Contains(ppUrl))
{
continue;
}
else//add a new navigation item
{
//not? add it.
nn = new SPNavigationNode(pp.Title, ppUrl);
nn = curWeb.Navigation.QuickLaunch.AddAsFirst(nn);
//here is the trick –
//We mark the navigation item as type “Page”.
nn.Properties["NodeType"] = "Page";
nn.Update();
}
curWeb.Update();
}

9 comments:

Tsahi said...

Hi Shay
Very interesting info.
How can I add sitemap to a MOSS sites?
Is it possible?

Shai Petel (Ben Shooshan) said...

Hi Tsahi,

Actually - we are releasing this week a utility that will run for a site collection and build a Google-Compatible site map.

Keep an eye out next week on our site for it http://www.kwizcom.com - i am not sure if it will be published as a product or a free utility.

Ted said...

You saved my day!

Pedro Castro (Bracnoria) said...

Thanks for the post. Look nice.
But, i try to use the "trick" only in a foreach loop retrieving all QuickLaunch nodes and it works!

In my case no need more code.


foreach (SPNavigationNode navNode in web.Navigation.QuickLaunch)
{
navNode.Properties["NodeType"] = "Page";
navNode.Update();

// Perform any operation you want with the node.
}


I hope this helps anyone.

Pedro Castro. (SharePoint 2003-2007 MCTS)
www.bracnoria.com
www.kaldeera.com

Christoph Herold said...

Thank you so much for your post!

This same procedure can be applied to webs too. All you must do is change the "NodeType" from "Page" to "Area". Also, you will probably use the publishingWeb.CurrentNavigationNodes and publishingWeb.GlobalNavigationNodes collections for adding/moving the nodes. But the process is the same.

Kudos
Christoph

Max Remkes said...

Have you tried to check in the newly created page?

nn.CheckIn("");

The navigation items will be updated after this statement.

Shai Petel said...

Hi Max,

Thank you for your post - that is also interesting.

Although in my scenario the pages are checked in already and I had no intention to check-out and check-in as it would change the page item itself (modified date for example).

In case you dont want to change the page itself, using the navigation trick I showed here was the only way I found to do that.

Thanks, Shai.

Нігер said...

Breadcrumb will showing invalid path after this update.

Shai Petel said...

Нігер,

I didnt encounter any issue with breadcrumb navigation after this update.

What is the invalid path you are reffering to?

If you have the wrong URL, I suggest you make sure your SharePoint is configured correctly with the right host headers, or try to use server relative paths instead of full URL (pp.Uri.AbsolutePath).

Again, if using the full URL gives you broken links, it suggests your SharePoint and IIS are not configured properly.

Good luck!