RSS Feed Feed your read!

Scot Hillier is an independent consultant and Microsoft SharePoint Most Valuable Professional focused on creating solutions for Information Workers with SharePoint, Office, and related .NET technologies. A frequent speaker at TechEd and SharePoint Connections, he is also the author many books on Microsoft technologies including 5 for SharePoint 2010. Scot splits his time between consulting on SharePoint projects and training for Critical Path Training. Scot is a former U. S. Navy submarine officer and graduate of the Virginia Military Institute. Scot can be reached at scot@shillier.com

Archives

January 2010 (17)
February 2010 (1)
March 2010 (1)
April 2010 (2)
May 2010 (1)
June 2010 (1)
August 2010 (4)
September 2010 (6)
October 2010 (3)
November 2010 (2)
February 2011 (1)
March 2011 (4)
April 2011 (3)
May 2011 (1)
August 2011 (1)

Links

Todd Baginski's Blog
BCS Team Blog
SharePoint Team Blog
SharePoint Workspace Team Blog
Andrew Connell's Blog
Ted Pattison's Blog

Tag Cloud

Business Data Connectivity, Claims, Conferences, MOSS 2007 Archived Post, PowerShell, Search, SharePoint 2010,

Cross-Site Collection Navigation 

Tags: Search

I recently had to create an intranet portal that consisted of a separate Site Collection for each department. This is a pretty common configuration. It has the advantages of allowing for a separate content database for each Site Collection and separate security boundaries. The disadvantage is that there is no out-of-the-box mechanism for navigating between Site Collections.
 
Sahil Malik has done some nice work implementing a solution using a site map and a web part. He also has a Codeplex project with the source available.
 
I liked Sahil's solutions, but I am a real advocate of using the search engine to solve problems, so I set out to create a solution based on search. The plan was to create a server control that made a search query to return all the Site Collections that the current user could access. I would then put the server control in the master page so it was available throughout all the Site Collections.
 
The basis of the entire solution revolves around the fact that search will return all of your Site Collections using the simple query contentclass:STS_Site. Go ahead and try it. Just type that into the keyword box in any Search Center. In my case, I used the full-text version of the query, which is:
 
SELECT url, title FROM Scope() WHERE "scope" = 'All Sites' AND contentclass = 'STS_Site'
 
Making use of the FullTextSqlQuery class, I created the server control with the following code:
 
namespace Hillier.Navigation.DelegateElements
{
    public class DelegateNavPart : WebControl
    {
        const string queryString = "SELECT url, title " +
                     "FROM Scope() " +
                     "WHERE \"scope\" = 'All Sites' " +
                     "AND contentclass = 'STS_Site'";
        DropDownList portalList;
        protected override void OnLoad(EventArgs e)
        {
            this.EnsureChildControls();
           
            if (!Page.IsPostBack)
            {
                SearchServiceApplicationProxy proxy = (SearchServiceApplicationProxy)SearchServiceApplicationProxy.GetProxy(SPServiceContext.GetContext(SPContext.Current.Site));
                FullTextSqlQuery keywordQuery = new FullTextSqlQuery(proxy);
                keywordQuery.ResultsProvider = SearchProvider.Default;
                keywordQuery.ResultTypes = ResultType.RelevantResults;
                keywordQuery.EnableStemming = false;
                keywordQuery.TrimDuplicates = true;
                keywordQuery.QueryText = queryString;
                ResultTableCollection results = keywordQuery.Execute();
                ResultTable result = results[ResultType.RelevantResults];
                portalList.Items.Add(new ListItem() { Selected = true, Text = "Select a portal", Value = "#" });
                List<PortalNavNode> portals = new List<PortalNavNode>();
                if (result.RowCount > 0)
                {
                    while (result.Read())
                    {
                        portals.Add(new PortalNavNode() { Title = result.GetString(1), Url = result.GetString(0) });
                    }
                }
                var q = from p in portals
                        orderby p.Title
                        select p;
                foreach (var i in q)
                {
                    portalList.Items.Add(new ListItem() { Selected = false, Text = i.Title, Value = i.Url });
                }
            }
        }
        protected override void CreateChildControls()
        {
            this.EnableViewState = true;
            portalList = new DropDownList();
            portalList.AutoPostBack = true;
            portalList.EnableViewState = true;
            portalList.SelectedIndexChanged += new System.EventHandler(portalList_SelectedIndexChanged);
            this.Controls.Add(portalList);
        }
        void portalList_SelectedIndexChanged(object sender, System.EventArgs e)
        {
            SPUtility.Redirect(portalList.SelectedItem.Value, SPRedirectFlags.Trusted, HttpContext.Current);
        }
        protected override void Render(HtmlTextWriter writer)
        {
            writer.Write("<div id=\"DelegateNavDivMain\" style=\"text-align:right;width=100%;background:#21344a\">");
            portalList.RenderControl(writer);
            writer.Write("</div>");
        }
    }
    public class PortalNavNode
    {
        public string Title { get; set; }
        public string Url { get; set; }
    }
}
 
Notice that I run the query and collect all the results in custom objects. Then I can use a simple LINQ query to sort the results so they appear alphabetically. Because the query is security-trimmed, users only see sites that they have permission to access. Selecting a Site Collection from the list causes a redirect to the home page for that Site Collection.
 
Using the server control in the intranet was as easy as replacing a delegate control in the master page. To accomplish this, I built a feature that put my control in the "GlobalNavigation" delegate control. Here's the elements.xml for the feature.
 
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="
http://schemas.microsoft.com/sharepoint/">
  <Control Id="GlobalNavigation" Sequence="99" ControlClass="Hillier.Navigation.DelegateElements.DelegateNavPart" ControlAssembly="$SharePoint.Project.AssemblyFullName$">
  </Control>
</Elements>
 
Because it was so easy, I also made a web part based on the same code. This allows me to put the control either in the master page or a content page.
 
You can download the code here.
 
Posted by Scot Hillier on 17-Mar-11
0 Comments  |  Trackback Url  |  Link to this post | Bookmark this post with:        
 

Links to this post

Comments