Get Current Navigation items for all site collections and webs - SP2010 - sharepoint

I'm trying to make a site map for a Sharepoint 2010 that will list every site and that site's current nav items. Navigation between sites is done via the top nav. However, I'm having problems getting the current nav items for each site. It appears that each time I get the PortalSiteMapProvider for the web, it returns the provider for the whole site collection. How do I get the items for just that site? I run this code for each site I want to get the items for:
var provider = new PortalSiteMapProvider();
provider.NavigationType = PortalNavigationType.Current;
provider.CurrentWeb = web;
var rootNode = provider.RootNode;
Anyone have any idea where to go from here?
EDIT:
So it isn't possible to just access each site collection and run this code. The PortalSiteMapProvider doesn't allow it. It is, however, possible to run this code in a ashx that's located in the layouts folder. I just need to access that ashx file from the site collection I want to enumerate, and it will work just fine :)

private void DrawWeb(SPWeb web, TreeNode node)
{
SPWebCollection webCol = web.Webs;
foreach (SPWeb w in webCol)
{
var n = new TreeNode(w.Title);
node.ChildNodes.Add(n);
DrawWeb(w, n);
w.Dispose();
}
}
Try calling this method like:
TreeNode webNode = new TreeNode(rootNode.Title);
DrawWeb(provider.CurrentWeb, webNode);
Hope this will be helpful.

So it isn't possible to just access each site collection and run this code. The PortalSiteMapProvider doesn't allow it. It is, however, possible to run this code in a ashx that's located in the layouts folder. I just need to access that ashx file from the site collection I want to enumerate, and it will work just fine :)

Related

Sharepoint 2010 Site Page with User Control = Blank Page

I have a problem with a site definition, it has several site pages, in those pages i need to have a top menu, so i created a user control, that draw that menu, and added it to the pages.
After deploying the solution i can create the site just fine.
In the initial phase, i have hard coded values for the menu links, and in each of the pages i could see the menu.
Now the menu it's dynamic, so i have a sharepoint list with the menu options, so i changed the code, instead of hard-coded values, i get them from a list, the code runs fine, i can check that while debugging, but in the end i get a complete blank page.
I can't interact with sharepoint data inside a user control that will be used in a site page?
I tested the control in a normal/application page and it works just fine, in both cases (hard-coded and sharepoint data).
A simple sample of the code:
protected void Page_Load(object sender, EventArgs e)
{
lblDate.Text = DateTime.Now.ToLongTimeString();
ddlData.Items.Clear();
//Option Manual - WORKS FINE
ddlData.Items.Add("Manual 1");
ddlData.Items.Add("Manual 2");
ddlData.Items.Add("Manual 3");
//Option Sharepoint Data - DOESNT WORK
SPSecurity.RunWithElevatedPrivileges(delegate () {
using (SPSite site = SPContext.Current.Site)
{
using (SPWeb web = site.RootWeb)
{
foreach (SPList lista in web.Lists)
{
ddlData.Items.Add(lista.Title);
}
}
}
});
}
The rest of the code, it's a simple page with the reference to this user control, and the aspx code of the user control only have the declaration of this two controls.
After several tests, i came to conclusion that i have two problems:
1 - In this project for some reason, unknown to me, inside the code someone had added some code to clear the errors, that's why i had the blank page.
2 - After seeing the error, i could confirm that if i use site pages and trying to get the data with Using, when it tries to dispose the objects it throws the exception, so the solution was simple, just remove the Usings.
Thanks

Update sharepoint list view programmatically

I have an aspx page with inline code in which I am trying to update the view programmatically by setting view's Query property to my CAML query. When I run with administrator user everything works perfect view get updated successfully but when I logged in with a user who belongs to visitor group and having read only access then I get an error on view.Update() line saying that:
"Unable to evaluate expression because the code is optimized or a native frame is on top of the call stack"
I have already try to run this code block with ElevatedPrivileges but not get any luck...(
following is my code which make you more clear:
SPUserToken token = CurrentSite.SystemAccount.UserToken;
using (SPSite st=new SPSite(SPContext.Current.Web.Url,token))
{
st.AllowUnsafeUpdates = true;
using (SPWeb wb=st.OpenWeb())
{
wb.AllowUnsafeUpdates = true;
vwSearchResult.Query = Query;
vwSearchResult.Update();
}
}
What you are doing here, is modifying the definition of the view for ALL users of the website, not only the current rendering instance of the page. This is why simple visitors cannot change it (they do not have such permission in the web)
If you want to do something, using the "SystemAccount" token, you have not only to do the "using SPSite, using SPWeb", but also find the list and the view using the "strong" SPWeb objects
Instead of modifying a view definition at runtime, you might want to consider using ListViewByQuery class http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.webcontrols.listviewbyquery.aspx

WebPart "metadata"?

I have not worked with webparts for sharepoint before, but need to make change to a webpart, that needs to be propagated to some 700 websites. It is a change to one of the properties of the webpart, the value needs to be changed. Is there a way to get metadata for a webpart and change it directly in the database (I assume that is where it is stored.)?
Here is the scenario: Webpart contains a comma delimited list of document types (internal type) that it should display. Now there are new doc. types that need to be added to all 700 websites. I need a way to enumerate websites, get the webpart metadata, and add these new doc types to webpart. Currently they go manually to each website, click on edit, type in new doc type, and save it.
As others have said the correct approach is to programmatically achieve this rather than edit the content database which will make your installation unsupportable. I regularly use a console application to do this in a site collection made up of sites created from a site template.
Here is an example that changes the Title property of a ListViewWebPart. Updated to include code for recursive loop. I haven't tested this but it should work.
private static void ProcessSiteCollection(string url)
{
using (SPSite siteCollection = new SPSite(url))
{
SPWeb rootWeb = siteCollection.RootWeb;
ProcessWebs(rootWeb);
}
}
private static void ProcessWebs(SPWeb parentWeb)
{
foreach (SPWeb web in parentWeb.Webs)
{
try
{
UpdateWebPart(web); // Set web part properties
ProcessWebs(web); // Recursively loop through children
}
finally
{
web.Dispose();
}
}
}
private static void UpdateWebPart(SPWeb web)
{
using (SPLimitedWebPartManager webPartManager =
web.GetLimitedWebPartManager("default.aspx", PersonalizationScope.Shared))
{
try
{
foreach (WebPart webPart in webPartManager.WebParts)
{
if (webPart.Title == "My Web Part")
{
ListViewWebPart listViewWebPart = (ListViewWebPart)webPart;
listViewWebPart.Title = "Updated Web Part";
webPartManager.SaveChanges(listViewWebPart);
web.Update();
break;
}
}
}
finally
{
webPartManager.Web.Dispose();
}
}
}
Directly accessing the sharepoint content databases is a big "no no." That's the official answer. :)
That being said, I have only ever looked in the content databases and never tried to actually change anything manually.
My suggestion, would be to modify the existing web part to modify the property based on currently set property(s). (I am assuming that some currently set property is invalid or needs to be updated based on changes to the infrastructure.) ... If this is the case, you can validate the property; making sure that current property is changed to what it needs to be, and/or making sure future property changes are valid.
Good luck!
DON'T
Seriously, do not go into the content databases and edit it. That way you are not supported anymore if anything should happen and Microsoft will not support you anymore (not until you revert the database back to an earlier version from a backup that is).
You can use the API to access webparts in your sites, here's some code that should get you started:
Enumerate page webparts

PortalSiteMapProvider

Everytime I instantiate a PortalSiteMapProvider instance it always defaults to the Root Web Site Collection.
I want to be able to acquire a site map from a different collection from an application page.
Because of this, the context defaults to the root web since this application page resides under _layouts and is not hosted under any site collection.
How do I acquire an instance to a site collection other than the root web from an application page?
Thanks.
You can't read another site collection's hierarchy through a PortalSiteMapProvider. E.g., if you are in Site Collection A, PSMP will only traverse A's tree, not B. You have to be in B to traverse B.
A workaround I implemented was to write a simple web service that runs in a site collection and takes a path as a parameter. It reads its own PSMP and writes an XML tree from that point in the hierarchy. Calling the web service living in SC-B from code running in SC-A is extremely fast, especially since the PSMP can rip through B's structure so quickly.
Edit:
Here are instructions for creating a web service in WSS3/MOSS.
Here is some totally non-functional code to get you headed in the right direction:
//method exposed through the web service
public XmlDocument GetTree(string path)
{
PortalSiteMapProvider psmp = PortalSiteMapProvider.GlobalNavSiteMapProvider;
SiteMapNode node = psmp.FindSiteMapNode(path);
return BuildXmlFromNode(node);
}
private XmlNode BuildXmlFromNode(SiteMapNode node)
{
XmlDocument xml = new XmlDocument();
reutrn BuildXmlFromNode(node, xml);
}
//recurses down the sitemapnode children
private XmlNode BuildXmlFromNode(SiteMapNode node, XmlNode xml)
{
XmlElement element = doc.CreateElement("Node")
element.SetAttribute("title", node.Title);
element.SetAttribute("url", node.Url);
xml.AppendChild(element);
foreach(SiteMapNode childNode in node.ChildNodes)
{
BuildXmlFromNode(childNode, element);
}
return xml;
}
Please note if you don't set a limit on the number of recursions and your site map hierarchy is very deep and/or wide, this could produce a HUGE xml document. Use with caution!

How do I programatically turn off show pages in navigation for sharepoint

I am progamatically creating a SharePoint site using
SPWeb spWeb = spSite.AllWebs.Add(...);
What code do I need run to set the spWeb to turn off the "Show pages in navigation" option?
Answer:
publishingWeb.IncludePagesInNavigation = false;
Wasn't sure myself but I was able to locate this:
Modifying navigation is another common
branding task since it affects what
users can see and how they can proceed
through a site hierarchy. The
Microsoft.SharePoint.Publishing
namespace exposes several classes that
target the Publishing site
infrastructure, such as PublishingWeb
and PublishingPage. Using these
classes, we can easily modify
navigation for each site. If you want
a child Web to display as a root level
site in global navigation, first turn
off inheritance from the parent site,
like so:
publishingWeb.InheritGlobalNavigation = false;
You might also want to hide all site
pages from global navigation. Setting
IncludePagesInNavigation to false
hides all pages in the site,
regardless of whether the
PublishingPage.IncludeInGlobalNavigation
property is set to true
// do not show pages in navigation
publishingWeb.IncludePagesInNavigation = false;
If you are dealing with default sites
that don't inherit from PublishingWeb,
it's still possible to hide these
sites from the global navigation bar.
For example, if you create a site
collection using the collaboration
portal template and want to exclude
the News site from global navigation,
add that site to the
__GlobalNavigationExcludes property of the site:
string globalNavExcludes = String.Empty;
SPWeb webSite = MSDNSiteCollection.RootWeb;
// _GlobalNavigationExcludes property contains a delimited string of
// GUIDs identifying the Id of each site to be excluded from global
// navigation
if (webSite.AllProperties.ContainsKey("__GlobalNavigationExcludes")) {
globalNavExcludes =
webSite.AllProperties["__GlobalNavigationExcludes"].ToString();
}
SPWeb newsSite = MSDNSiteCollection.AllWebs["News"];
// string is delimited "{GUID};{GUID};",
// use format code B to convert to string
globalNavExcludes += String.Concat(currentWeb.ID.ToString("B"), ";");
webSite.AllProperties["__GlobalNavigationExcludes"] = globalNavExcludes;
webSite.Update();
Adding navigation nodes directly to an
SPNavigationNodeCollection is a good
way to display only the nodes you want
as well as to group nodes and links to
external sites. Figure 10 shows how to
add an internal link, external link,
and a heading to the global navigation
bar. This example addresses some of
the properties of the SPNavigation
class that affect whether the link
opens in a new window and how to
handle empty URLs.
For SP 2010 use below...
publishingWeb.Navigation.GlobalIncludePages = false;

Resources