PortalSiteMapProvider - sharepoint

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!

Related

How to render a template by name?

I am trying to get my head around ServiceStack self-hosted app and the new API.
Adding two views of the same name in separate folders results in an error at startup. Is this not allowed?
Foo\
Index.cshtml
Bar\
Index.cshtml
Is there a way to specify a template via a decorator on a method or directly as a return value? I know about the convention of naming views after DTOs. I prefer to be more explicit or follow a convention closer to Sinatra/Express.
return Render(typeof(Views.Foo.Index), new { Name = "Nelly" });
The ServiceStack's Razor Rockstars website which holds the documentation for Razor support in ServiceStack lists some options for selecting a different template:
If it doesn't follow the convention (i.e. Request or Response DTO name) then you can dynamically specify which view or layout template gets used by returning a decorated HttpResult like:
return new HttpResult(dto) {
View = {viewName},
Template = {layoutName},
};
If you're using a static view (i.e. service always uses the same view) then you can specify what view to use by decorating it with the [DefaultView] attribute
[DefaultView("Rockstars")]
public object Get(Rockstars request) {
...
return responseDto;
}
In either case, if you want it strong-typed you can use something like typeof(RequestDto).Name.
View names must be unique
Unlike MVC, heirachy's does not influence view selection in ServiceStack and because each View Page (i.e. razor pages in the /Views folder) must be unique, you're free to lay them out in any flat or nested folder structure you wish.

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

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 :)

Sharepoint: How to obtain the current site/web/list properly

What is the best way to obtain the current site/web/list ?
Option 1 - Reusing existing objects
SPSite site = SPContext.Current.Site;
SPweb web = SPContext.Current.Web;
SPList list = SPContext.Current.List;
Option 2 - Creating new objects
SPSite site = new SPSite(SPContext.Current.Site.ID); // dispose me
SPweb web = site.OpenWeb(SPContext.Current.Web.ID); // dispose me
SPList list = web.Lists[SPContext.Current.List.ID];
I experienced problems when using option 1 in some situations. Since then I chose the 2nd option and it worked fine so far.
What is your opinion on this? I is generally better to go with option 2? Other suggestions?
Do use Option one, because it's more resource-efficient as you don't need to create new object (For example OpenWeb method involves querying database to do it's job). But you are not allowed to dispose objects from SPContext, that will definitely cause you problems.
You must use Option Two if your code is not run in context of application pages (like SharePoint timer or Workflow), because SPContext.Current object will be null.
Link
And yes, if you open SPWeb or SPSite object, you MUST dispose it.
I normally go with option 2 (which I believe si the approach recommended by Microsoft), but I tend to wrap things using using to ensure they are disposed of properly. Example:
using (SPSite site = new SPSite("MY SITE URL"))
{
using (SPWeb web= site.OpenWeb())
{
// Do stuff
}
}
This approach allows you to be explicit about when objects are created and destroyed.

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

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