Cross-Site Collection Navigation

Scot Hillier

by Scot Hillier on 3/17/2011

Share this:

Article Details

Date Revised:

Applies to:
Article, Content Type, EDITORIAL USE ONLY

Originally published on 3/17/2011 and reproduced here for reference

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)

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

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\">");

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="">
<Control Id="GlobalNavigation" Sequence="99" ControlClass="Hillier.Navigation.DelegateElements.DelegateNavPart" ControlAssembly="$SharePoint.Project.AssemblyFullName$">

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.


Topic: Article

Sign in with

Or register