Restoring the top navigation bar of a SharePoint site

It’s a known issue that if you delete the “Top Navigation” menu through the API in SharePoint 2007, you cannot restore it easily. I came across this problem in one of my development sites. When looking for a solution, I found this article, saying you have to do it through the database.

The code below solves the problem programmatically (and yes, it changes the database directly). Of course, it’s an unsupported solution – if you use it, you may get in trouble with Microsoft official support.

string sSiteUrl = "http://moss/sites/yoursite/";
//after the code gets executed, we have to dispose the 
//spweb object, since the Navigation object of the current SPWeb instance will contain wrong information
using (SPSite oSite = new SPSite(sSiteUrl))
    using (SPWeb oWeb = oSite.RootWeb)
        //check if the top navigation bar is already there
        SPNavigationNode oTopnav = null;
            oTopnav = oWeb.Navigation.GetNodeById(1002);
        catch (Exception)
            oTopnav = null;

    if (oTopnav == null)
        //we've found out there is no top navigation bar. Let's create one;

        //we create a temporary navigation node pointing back to web site root
        Microsoft.SharePoint.Navigation.SPNavigationNode oTempNode = new Microsoft.SharePoint.Navigation.SPNavigationNode("Navigation", oWeb.ServerRelativeUrl);
        //we add this node to the "global" collection (where quick launch and top nav bar normally live)

        //now the dirty work - go to the database and change the ID of the navigation node
        System.Data.SqlClient.SqlConnection oConn = new System.Data.SqlClient.SqlConnection(oSite.ContentDatabase.DatabaseConnectionString);
        System.Data.SqlClient.SqlCommand oCmd = new System.Data.SqlClient.SqlCommand();
        oCmd.Connection = oConn;
        oCmd.CommandType = System.Data.CommandType.Text;
        //we only update the node which has the same ID as the one we just created, but, to be completely sure that 
        //nothing else gets changed, we add a requirement to have the same siteid and webid
        oCmd.CommandText = @"UPDATE NavNodes SET Eid=1002 WHERE (Eid=" + oTempNode.Id.ToString() + 
             @") AND (SiteId='" + oSite.ID.ToString("D") + "') AND (WebId='" + oWeb.ID.ToString("D") + "')"; ;
        //closing the connection

} //the oSite object is disposed here