none
How to programatically enumerate web part zones outside of the page context

    Question

  • I need to be able to identify the webpart zones available in pages while I am enumerating sites/subsites.  For example, I want to loop through each site in my farm and list the available webpart zones for each site.  So far I have found lots of code to allow me to list the zones for the page I have my webpart deployed in, but this is not my requirement. I need to reach out to toher pages in other sites and list all of their webpart zones.

    I found a similar topic on this forum: "Need to list Web Part Zones Programmatically Without Page Context", which I believe is still unresolved.

    Does anyone know if this is possible?  Is the information for each sites zones only contained in the WebPartManager, and if so, is is possible to access and read a WebPartManager if your webpart is not on that particular page?

     

    Tuesday, November 23, 2010 5:23 PM

Answers

  • Hi jmbei,

     

    I think the reason is that the “/_catalogs/masterpage/{filename}.aspx” is in the root web of the site. But the “Web” object is not the root web now. So I think you can try to do as following:

    1.       Change the “web” to “SPSite.RootWeb”, and the code will likes:

    SPFile pLayoutFile = SPSite.RootWeb.GetFile(pPage.Layout.ServerRelativeUrl);

    2.       Open the “/_catalogs/masterpage/{filename}.aspx” page using SPD (SharePoint Designer 2010). And then try to find the “webpartzone”. You’d better backup the page before deal with it.

     

    Best Regards,

    Wayne

    TechNet Subscriber Support in forum

    If you have any feedback on our support, please contact tngfb@microsoft.com


    SharePoint 2010
    • Marked as answer by jmbei Friday, December 03, 2010 4:02 PM
    Friday, December 03, 2010 1:25 AM

All replies

  • Recursively iterate the SPSite & SPWeb, get PublishingWeb from the SPWeb, get the target page as publishing page and iterate the web part zones

    SPSite siteCollection = new SPSite(webSite.Site.Url);
    SPWeb webSiteHigh = siteCollection.OpenWeb(relativeUrl);
    PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(webSiteHigh);
    SPList pagesList = webSiteHigh.Lists["Pages"];
    SPListItem page = pagesList.Items[pageGuid];
    PublishingPage pubPage = PublishingPage.GetPublishingPage(page);
    
    using (SPLimitedWebPartManager wpm = web.GetLimitedWebPartManager(pubPage .Url, PersonalizationScope.Shared))<br/>   {
       
       //iterate the web partzones here
       }
    

     

     


    Aravind http://aravindrises.blogspot.com
    Tuesday, November 23, 2010 5:49 PM
  • Hi jmbei,

    Thanks for your post.

    What's your idea about the replies? Is it helpful?

    Thanks.


    SharePoint 2010
    Thursday, November 25, 2010 2:00 AM
  • Hi,

    Thanks Aravindlive, and sorry for the late response; I was away for a couple of days.  Looking at your code I think I see how it will work for me; however, I have a couple of questions:

    1. with SPSite siteCollection = new SPSite(webSite.Site.Url), do I just replace webSite.Site.Url with the URL string (i.e http://mysite.com/Site1/Subsite2)?
    2. where do I obtain relativeUrl in this statement: SPWeb webSiteHigh = siteCollection.OpenWeb(relativeUrl)?
    3. in your using statement you refer to web.GetLimitedWebPartManager(, where does web come from?

    Thank you again for this suggestion.  I will play with it today, and if I answer my own questions I will let you know.

    ---------------------------------------------------------------

    I've been playing with your code a bit, and am getting an error at SPList pagesList = webSiteHigh.Lists["Pages"], which I'm trying to sort out.

    One question I do have is what do I call to enumerate the zones in the WebPartManager?  The SPLimitedWebPartManager doesn't seem to have access to the Zones collection, as far as I can see.

    Thanks!

    ----------------------------------------------------------------------------------

    Another update ...

    I resolved the ["Pages" issue - dumb on my part - my dev environent is on SP 2010, but our deployment is 2007. There's no "Pages" in 2010.  I'm still stuck getting at the Zones collection for a page referenced via URL.  I can easily use the SPWebPartManager to find the WebPartmanager control on the page I have the webpart embedded in, and then enumerate the zones withing it.  But I can't find a way to hit the zones collection on a page referenced via URL.

    Thanks again.

     

     

    Thursday, November 25, 2010 3:40 PM
  • Hi jmbei,

     

    Where do you deploy the code to? Is it a SharePoint Project or Console Application project?

    If you deploy the code to SharePoint site, like using Web Part, you can use the code below to get SPSite and SPWeb object;

    SPSite oSiteCollection = SPContext.Current.Site;

    SPWeb oWebsite = SPContext.Current.Web;

     

    Otherwise, you have to get the SPWeb like this:

    using(SPSite oSiteCollection = new SPSite("http://Server_Name"))

    {

        SPWebCollection collWebsites = oSiteCollection.AllWebs; 

        foreach (SPWeb oWebsite in collWebsites)

        {

            Console.WriteLine("Web site: {0}", oWebsite.Url);

            oWebsite.Dispose();

        }

    }

     

    As your third question, I think the “web” should be replace with “publishingWeb”.

     

    For your last question, there is a similar issue, hope it’s helpful.

    http://stackoverflow.com/questions/541384/how-to-get-all-webpartzones-on-a-page-in-sharepoint-2007

     

    Best Regards,

    Wayne

    TechNet Subscriber Support in forum

    If you have any feedback on our support, please contact tngfb@microsoft.com


    SharePoint 2010
    Monday, November 29, 2010 1:45 AM
  • Hi Wayne,

    Thanks for this.  I did already figure out the answer you provide for the first part, and had also previously read the link you provided. In that link the code is reliant on being in the same page that you want to analyse for web part zones:

    WebPartPage l_oPage = (WebPartPage) this.Page;

    SPWebPartManager l_oManager = (SPWebPartManager) l_oPage.Master.FindControl(this.SPWebPartManager); 

    What I need to do is try to replace the "this" reference in the above with a direct reference to another page.  for example, if the page I wanted to enumerate for zones was at http://mysite/Pages/default.aspx, I would like to set a variable reference to that and effectively use it to replace the "this" reference.

     

    targetPage =  "http://mysite/Pages/default.aspx";

    WebPartPage l_oPage = (WebPartPage) targetPage.Page;

     SPWebPartManager l_oManager = (SPWebPartManager) l_oPage.Master.FindControl(targetPage.SPWebPartManager); 

     I have not found a way to get this accomplish this.

    Thanks,

    Jim

    Monday, November 29, 2010 3:30 PM
  • Hi jmbei,

     

    Thanks for your post.

    I find it’s impossible to get the web part zones using SharePoint Object Model directly after some research. A workaround about this is using the Source Code to find the webpartzone string.

    Here are the follow steps:

    1.       Get the SPFile Om using the web.GetFile() method;

    2.       Get the Source code of the file using the System.Text.Encoding.UTF8.GetString() method;

    3.       Find the data (the web part zone string likes: <WebPartPages:WebPartZone runat="server" title="loc:TitleBar" id="TitleBar" AllowLayoutChange="false" AllowPersonalization="false"/>) what you need.

     

    Here is some sample code.

    public void GetWebPartPage(SPWeb web)

    {

                    SPFile file = web.GetFile("SitePages/test.aspx");

                    string sourceCode = System.Text.Encoding.UTF8.GetString(file.OpenBinary());           

                    List<string> webPartZoneStr = new List<string>();

                    FindWebPartString(webPartZoneStr, sourceCode);

                    foreach (string str in webPartZoneStr)

                    {

                                    Console.WriteLine(str);

                    }           

    }

     

    private void FindWebPartString(List<string> strCol, string src)

    {

                    if (src.IndexOf("<WebPartPages:WebPartZone") > -1)

                    {

                                    src = src.Substring(src.IndexOf("<WebPartPages:WebPartZone"));

                                    strCol.Add(src.Substring(0, src.IndexOf("/>") + 2));

                                    string newStr = src.Substring(src.IndexOf("/>") + 2);

                                    FindWebPartString(strCol, newStr);

                    }

                    else

                    {

                                    return;

                    }

    }

     

    Hope it can solve your issue.

    Best Regards,

    Wayne

    TechNet Subscriber Support in forum

    If you have any feedback on our support, please contact tngfb@microsoft.com


    SharePoint 2010
    Tuesday, November 30, 2010 9:17 AM
  • Hi Wayne,

    Thanks for this info.  I've been coming to the conclusion that what i want to do is impossible through a simple API call.

    I will play with your sample code tomorrow and let you know what success I had.

    Cheers,

    Jim

    Tuesday, November 30, 2010 10:29 PM
  • Hi Wayne,

    I've tried out your code, and am not getting the webpart zones.  The reason is that the string that gets passed to FindWebPartString(src) contains only the <@ ... /> tags and the contents between <head> <head/>.  Being relatively new to this level of Sharepoint programming I'm missing something in my understanding of the architecture.  Here is an example of the string passed to src:

    <%@ Page Inherits="Microsoft.SharePoint.Publishing.TemplateRedirectionPage,Microsoft.SharePoint.Publishing,Version=12.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" %> <%@ Reference VirtualPath="~TemplatePageUrl" %> <%@ Reference VirtualPath="~masterurl/custom.master" %>
    <html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"><head>
    <!--[if gte mso 9]><xml>
    <mso:CustomDocumentProperties>
    <mso:ContentType msdt:dt="string">Welcome Page</mso:ContentType>
    <mso:PublishingPageLayout msdt:dt="string">/_catalogs/masterpage/DepartmentsHome.aspx, /_catalogs/masterpage/DepartmentsHome.aspx</mso:PublishingPageLayout>
    <mso:PublishingPageContent msdt:dt="string"></mso:PublishingPageContent>
    </mso:CustomDocumentProperties>
    </xml><![endif]-->
    <title>Board of Trustees</title>
    
    <!--mstheme--><link rel="stylesheet" type="text/css" id="onetidThemeCSS" href="../_themes/Scholaris/Scho1011-65001.css"><meta name="Microsoft Theme" content="Scholaris 1011, default">
    </head>
    

    I believe there is some sort of redirection occurring to provide the actual page content which contains the <WebPartPages:WebPartZone> tag. I've looked in the custom master page and the various site templates, but haven't found a document containg the <webpartpages: .../> tag.  Yet, if I use code to enumerate zone ID's from installed webparts on a page I am successful.

    I'm researching to try to find the answer, but if you know off the top of your head any guidance would be greatly appreciated.

    Thanks,
    Jim

    Wednesday, December 01, 2010 4:11 PM
  • Hi jmbei,

     

    From the source code, I think you are using the Publishing Site, am I right?

     

    If yes, you can get the Layout Page of the page, and then find the “webpartzone” in its source code.

    public void GetMasterPage(SPWeb web)

    {

                    PublishingSite pSite = new PublishingSite(web.Site);;

                    PublishingWeb pWeb = PublishingWeb.GetPublishingWeb(web);

                    SPQuery query = new SPQuery();

                    query.Query = " <Where><Eq><FieldRef Name=\"Title\" /><Value Type=\"Text\">Test</Value></Eq></Where>";

                    PublishingPageCollection pPageColl =

                                    pWeb.GetPublishingPages(query);

     

                    if (pPageColl.Count == 1)

                    {

                                    PublishingPage pPage = pPageColl[0];

                                    SPFile pLayoutFile = web.GetFile(pPage.Layout.ServerRelativeUrl);               

                                    string sourceCode = System.Text.Encoding.UTF8.GetString(pLayoutFile.OpenBinary());

                                    //FindWebPartString();

                    }

                    else

                    {

                                    return;

                    }             

    }

     

    More information about PublishingWeb. GetPublishingPages()

    http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.publishing.publishingweb.getpublishingpages(v=office.12).aspx

     

    Best Regards,

    Wayne

    TechNet Subscriber Support in forum

    If you have any feedback on our support, please contact tngfb@microsoft.com


    SharePoint 2010
    Thursday, December 02, 2010 6:46 AM
  • Hi Wayne,

    Thanks for this.  Still having problems though. on this line:

    SPFile pLayoutFile = web.GetFile(pPage.Layout.ServerRelativeUrl);
    

    I get "Value does not fall within the expected range." error.  The server relative URL of pPage is correct.  I've been trying a few things to resolve it, but I am not sure what would be causing that error.

    pPage.Layout.ServerRelativeURL returns: /_catalogs/masterpage/{filename}.aspx, filename being dependant on the site.  I thought perhaps it needed some modification of the returned URL string, but none of the variants I tried worked.

    Any thoughts?

    Thanks,
    Jim

    Thursday, December 02, 2010 8:20 PM
  • Hi jmbei,

     

    I think the reason is that the “/_catalogs/masterpage/{filename}.aspx” is in the root web of the site. But the “Web” object is not the root web now. So I think you can try to do as following:

    1.       Change the “web” to “SPSite.RootWeb”, and the code will likes:

    SPFile pLayoutFile = SPSite.RootWeb.GetFile(pPage.Layout.ServerRelativeUrl);

    2.       Open the “/_catalogs/masterpage/{filename}.aspx” page using SPD (SharePoint Designer 2010). And then try to find the “webpartzone”. You’d better backup the page before deal with it.

     

    Best Regards,

    Wayne

    TechNet Subscriber Support in forum

    If you have any feedback on our support, please contact tngfb@microsoft.com


    SharePoint 2010
    • Marked as answer by jmbei Friday, December 03, 2010 4:02 PM
    Friday, December 03, 2010 1:25 AM
  • Hi Wayne,

    Thank you thank you thank you!!!!!  This works like a dream.  I would have never figured this out in any reasonable timeframe without your help.

    There are a couple of pages on our site it isn't working for, but I think I have the understanding now to resolve them.

    Thank you again for all your help.  I really appreciate it.

    Cheers,
    Jim

    Friday, December 03, 2010 4:02 PM
  • Hi Wayne,

    Just a couple of quick questions before we close this thread.

    We have Sharepoint sites which are not PublishingWeb sites.  I still need to get the pages for these non-publishing sites to pass over to the method you've helped me build.  I've tried several things, but cannot seem to enumerate a pages collection for these sites.  Sometimes thee sites will have just a default.aspx page, but other times they will have 4 or 5 other aspx pages.

    As I understand it, along with Publishing sites, there are "normal" sites, list, blog and wiki sites. The tool I am building will need to read the pages in each type of site, and enumerate the available webpart zones to allow the user to select multiple sites/pages and zones to push webparts to.

    To make a short story long, I need to understand how to reference non-PublishingWeb sites, and to come up with equivalent statements to:

    pagesList = swpSite.Lists["Pages"]
    

    depending on the type of site it is.

    Thank you again for all of your assistance.  I'm a newbie to SharePoint webpart programming and you have saved me a HUGE amount of learning curve time.

    Cheers,
    Jim

    Friday, December 03, 2010 9:00 PM
  • Hi jmbei,

     

    Glad to hear the solution is helpful.

    Now, as I understand, you want to enumerate all the pages in the page libraries, am I right? The way is similar to get all the files in the document library. Here is some sample code, hope it’s helpful.

    public void GetAllPages(SPWeb web)

    {

                    SPDocumentLibrary docLib =

                                    (SPDocumentLibrary)web.GetList("SitePages");

                    foreach (SPFile file in docLib.RootFolder.Files)

                    {

                                    Console.Write(file.Name);

                    }

    }

    More information:

    http://blogs.msdn.com/b/peterj/archive/2008/01/23/creating-folders-and-adding-files-to-sharepoint-document-library.aspx?wa=wsignin1.0

     

    Best Regards,

    Wayne

    TechNet Subscriber Support in forum

    If you have any feedback on our support, please contact tngfb@microsoft.com

    Tuesday, December 07, 2010 1:50 AM
  • Hi Wayne, The issue is not getting documents from a library, instead it's getting at pages that are not PublishingWeb pages. For example, the code that I have working right now (thanks to your help!) can enumerate pages in a PublishingWeb site just fine, and if they are in the Pages list (i.e. http://mysite/SubSiteName/Pages/pagename.aspx), but fails for sites that are not PublishingWeb sites. These seem to have a URI which appears very normal, except they never have the Pages element (i.e. http://mysite/SubSiteName/pagename.aspx). I've tried a few things that I can think of, but basically what happens is that when testing to see if a site is a PublishingWeb I get a false: SPSite siteCollection = new SPSite(sURI); SPWeb swpSite = siteCollection.OpenWeb(); if (PublishingWeb.IsPublishingWeb(swpSite)) <- this test throws a false. This leads me to believe I need to handle these particular sites in our farm differently. There is also the case of managed paths: we have about 50 subsites which are "managed paths". I need to learn how to enumerate those, along with their related pages. I'm pretty certain that once I figure out how to enumerate non-PublishingWeb pages, I will be able to query the database to get the URI's of the managed paths sites, and then go and enumerate the pages under them. Thanks again for all your help in this. I really appreciate it. Cheers, Jim
    Thursday, December 09, 2010 4:06 PM
  • I've figured out the managed paths issue.  I'm not sure if it's the most elegant solution, but it's the only way I can see to do it so far.  I call site.WebApplication.Prefixes, and enumerate the returned list for the names of the managed paths.  I then go to the database with a query to pull the complete URL's for the managed paths, and use those to open the page.  I don't like doing direct database queries in a system like this, in case the undrelying data model gets changed by Microsoft with an upgrade/update, but I can't find an API call to do this.

    I'm also using the same principal to open non-PublishedWeb pages.  A bit kludgey, but it's working.

    Thanks again for all your help Wayne.  I've got this up and running now thanks to you!

    Cheers,

    Jim

    Monday, December 13, 2010 3:37 PM