October 2011

Volume 26 Number 10

SharePoint Development - Building Information Architecture in SharePoint 2010

By Shahram Khosravi, Ph.D. | October 2011

The release of Microsoft SharePoint 2010 saw new Enterprise Content Management (ECM) capabilities added to the collaboration software. This article shows you how to take advantage of these new features to build flexible, extensible and maintainable information architecture for the following two types of portals:

  • Internet/intranet/extranet-facing publishing portals 
  • Knowledge-management portals

I’ll walk you through the design and implementation of several custom SharePoint components that can be used to implement the information architecture for these portals.

Building Information Architecture for Internet/Intranet/Extranet-Facing Publishing Portals

Traditionally, Internet/intranet/extranet-facing publishing portals follow a strictly guided navigation model where GUI elements such as menus are used to guide the users to the content they’re looking for. Such portals normally have a top and a left navigation menu.

The top navigation menu is usually an AspMenu that allows users to make selections from the first and second tiers of the portal taxonomy. The left navigation menu is usually an AspMenu that allows users to make selections from the third and fourth (or possibly fifth) tiers of the portal taxonomy.

The entire content of the portal is divided into a set of categories that form the first tier. I’ll use the following naming convention for categories: Termi where itakes on an integer value. For example, the first and second categories are named Term1 and Term2, respectively.

The content in each category is then divided into a set of subcategories which form the second tier. I’ll use the following naming convention for subcategories: Termij where i and j take on integer values. For example, the first subcategory of the first category is named Term11.

This categorization of the content continues until we reach the desired granularity. Figure 1 shows this portal taxonomy.

Portal Taxonomy
Figure 1 Portal Taxonomy

Such portal taxonomy is normally implemented in SharePoint 2007 through establishment of the appropriate site structure. I’ll start by creating a separate site for each category. I’ll then create separate subsites under each category site for the respective subcategories, and so on. Figure 2 shows the site structure of the portal.

The Site Structure of the Portal
Figure 2 The Site Structure of the Portal

Basically, each category, subcategory, sub-subcategory and so on has its own dedicated site. This dedicated site contains a Pages document library that contains the pages for that site. If I were to use social networking language, I could say all pages in the Pages document library of a site are implicitly tagged with the category, subcategory or sub-subcategory that the site represents. In a way, I implicitly tag a bunch of pages with a category, subcategory or sub-subcategory by creating them in a site that represents that category, subcategory or sub-subcategory. This way, I create a new implicit tag (or term) by creating a new site.

The top navigation AspMenu displays these categories (first tier) and their subcategories (second tier). The user selects a category or subcategory from this AspMenu to navigate to the page that’s implicitly tagged with that category or subcategory.

When the user selects a subcategory from the top navigation menu, the left navigation AspMenu (Quick Launch) displays the sub-subcategories associated with that subcategory. The user selects a sub-subcategory from this AspMenu to navigate to the page that’s implicitly tagged with that sub-subcategory. The Pages document library of a given site may contain multiple pages, which means that multiple pages may be implicitly tagged with the same category, subcategory or sub-subcategory. The Quick Launch displays the links to all pages that are implicitly tagged with the same sub-subcategory.

Comparison of Figure 1 and Figure 2 clearly shows that the portal taxonomy or information architecture directly reflects the portal site structure. This makes the information architecture inflexible and introduces the following problems:

  • Creating a new implicit tag requires site administration permission to create a new site.
  • Retagging a page requires physically moving the page from one site to another.
  • Reorganizing the information architecture requires physically moving and deleting sites and pages.
  • Content authors aren’t able to share the same page across multiple categories, subcategories, sub-subcategories and so on. They have to create a separate page for each category, subcategory and so on because each is a separate site. The only out-of-the-box option is to copy the page to the respective sites. This solution introduces two usability problems. First, the content author has to copy the page to many different places. Second, each time the content author changes the content in one page, he has to go back to all those copies in all those sites to make the same updates or copy the page again. This is error-prone.
  • Because changing taxonomy requires structural changes in the site structure, which involves a lot of time and effort, taxonomy is very rigid. Taxonomy is tightly coupled with the way information is actually stored in the site collection.

Enter SharePoint 2010 ECM. You can now implement your portal taxonomy in a managed metadata service application where you manage it centrally. Such implementation no longer depends on your portal site structure:

  • You don’t need to provision new sites just to create new implicit tags. You simply add new terms to the desired spots in the portal taxonomy in the term store. This way, you can now keep all your pages in the same Pages document library in the same site because scalability of document libraries is no longer an issue in SharePoint 2010. This article assumes that all pages are maintained in a single Pages document library in a single site.
  • You can retag a page with a new term without having to physically move the page.
  • You can reorganize your taxonomy by simply reorganizing your taxonomy in the term store without having to physically move or delete sites and pages.
  • You can share the same page across multiple categories, subcategories and so on by simply tagging the page with multiple terms—where each term represents a category, subcategory and so on—without having to physically copy the page across sites.

I’ll implement the portal taxonomy as a single term set, as shown in Figure 3.

Implementation of Portal Taxonomy in the Managed Metadata Service Application
Figure 3 Implementation of Portal Taxonomy in the Managed Metadata Service Application

However, implementing the portal taxonomy in a term store instead of the portal site structure introduces two challenges discussed in the next sections.

The TaxonomyDataSource Control

First, the v4.master master page comes with an AspMenu that displays the top navigation menu. This control is bound to a site map data source control that uses a provider to retrieve the site map data from the portal site structure. This isn’t going to work in this case because I want the site map data to come from the term store, not the portal’s physical site structure.

One option is to implement a custom site map provider that retrieves the appropriate terms from the term store. Another option is to implement a custom data source control. I’ll use the latter because ASP.NET comes with a powerful data source control named XmlDataSource that I can easily extend to achieve my goal. I’ll name this custom data source control TaxonomyDataSource.

TaxonomyDataSource exposes a Boolean property named IsGlobal. I’ll include two instances of TaxonomyDataSource in the master page. One instance will be bound to the AspMenu that renders the top navigation menu. The IsGlobal property of this instance will be set to true to retrieve all terms of the term set that contains the portal taxonomy. This enables the top navigation menu to display all categories and their subcategories. Keep in mind that categories and their respective subcategories are nothing but the child and grandchild terms of this term set.

The second instance will be bound to the AspMenu that renders the left navigation menu (Quick Launch). The IsGlobal property of this instance will be set to false to retrieve only the subterms of the current term. Let me elaborate on what I mean by “current term.”

Recall that when the user selects a subcategory from the top navigation menu, the left navigation menu must display the list of sub-subcategories associated with that subcategory. The current term in this context is the subcategory. The second instance basically returns the current term’s child and grandchild terms, which are nothing but the sub-subcategories and sub-sub-subcategories.

TaxonomyDataSource overrides the Data property to retrieve the portal taxonomy from the term store. The main objective of the Data property is to create the XML document that XmlDataSource uses to create the nodes that it passes to AspMenu. This property first uses the SharePoint 2010 managed metadata API to access the term set that contains the portal taxonomy, as shown here:

TaxonomySession taxonomySession = new TaxonomySession(SPContext.Current.Site);
TermStore termStore = taxonomySession.TermStores[this.TermStore];
Group group = termStore.Groups[this.Group];
TermSet termSet = group.TermSets[this.TermSet];

The property uses all terms in the term set if IsGlobal is true:

termsToReturn = termSet.Terms;

If IsGlobal is false, the property first accesses the GUID of the current term, which is exposed through the CurrentTermId query string parameter. Then it uses the child and grandchild terms of the current term:

Term currentTerm = taxonomySession.GetTerm(
  new Guid(this.Page.Request.QueryString["CurrentTermId"]));
termsToReturn = currentTerm.Terms;

The Data property then starts creating the XML document. This document has a document element named <Terms>, which contains a hierarchy of <Term> elements. TaxonomyDataSource creates a separate <Term> element to represent each term that it pulls from the term store. Following is an example of such an XML document:

<Terms>
  <Term Name="Term1" URL="PageUrl?CurrentTermId=">
    <Term Name="Term11" URL="PageUrl?CurrentTermId=">
      <Term Name="Term111" URL="PageUrl?CurrentTermId="/>
    </Term>
  </Term>
  <Term Name="Term2" URL="PageUrl?CurrentTermId=">
    <Term Name="Term21" URL="PageUrl?CurrentTermId="/>
  </Term>
</Terms>

Note that the <Term> element has two attributes named Name and URL. The Name attribute is set to the name of the term and the URL is set to the URL of the page that’s tagged with that term. This URL includes a query string parameter named CurrentTermId that contains the GUID of the current term.

The Data property iterates through the retrieved terms and invokes the GetXmlFragment method for each enumerated term. The main goal of this method is to build the <Term> element that represents the enumerated term and this <Term> element’s <Term> subelements that represent the child and grandchild terms of the enumerated term:

foreach (Term term in termsToReturn)
{
  GetXmlFragment(publishingPageCollection, term, ref xml);
}

Note that TaxonomyDataSource uses the SharePoint publishing API to access the collection that contains the publishing pages.

Next, I’ll discuss the implementation of GetXmlFragment. However, first you need to understand the significance of the Tags site column, which is of type Managed Metadata. You’ll need to create this site column and bind it to the term set that contains the portal taxonomy and add the site column to the associated content type of the publishing page layout. The Tags column allows the content author to tag publishing pages with terms from the portal taxonomy.

GetXmlFragment consists of three parts. As Figure 4 shows, the first part searches through the pages in the Pages document library for the first page that’s tagged with the specified term and renders a <Term> element to represent the term.

Figure 4 The First Part of GetXmlFragment

foreach (PublishingPage publishingPage in publishingPageCollection)
{
  TaxonomyFieldValueCollection values =
    publishingPage.ListItem["Tags"] as TaxonomyFieldValueCollection;
               
  foreach (TaxonomyFieldValue value in values)
  {
    if (value != null && value.TermGuid == term.Id.ToString())
    {
      url = publishingPage.Uri.AbsoluteUri;
      xml += "<Term Name='" + term.Name + "' URL='" + url +
        "?CurrentTermId=" + term.Id.ToString() + "'>";
      closeTerm = true;
      defaultPublishingPage = publishingPage;
      break;
    }
  }
 
  if (closeTerm)
    break;
}

The URL attribute of this <Term> element is set to the URL of this page and the CurrentTermId query string parameter is set to the GUID of this term. This page acts as the default page for the specified term. This basically simulates the default page of a site if I were to create a site to represent the specified term.

The code in Figure 4 basically generates the following XML fragment:

<Term Name='TermName' URL='DefaultPageURL?CurrentTermId=GUID>

Note that the <Term> element isn’t closed yet because I still need to iterate through the child and grandchild terms of this term and render <Term> subelements within this <Term> element for each enumerated child and grandchild term. This is exactly what the second part of GetXmlFragment does:

foreach (Term cterm in term.Terms)
{
  GetXmlFragment(publishingPageCollection, cterm, ref xml);
}

GetXmlFragment finally closes the parent <Term> element. Next, GetXmlFragment renders <Term> elements for the rest of the pages in the Pages document library that are tagged with the same term. These <Term> elements are treated as the siblings of the <Term> element that represents the default page. This allows Quick Launch to render the links to all pages that are tagged with the current term. This basically simulates the non-default pages of a site if I were to represent the term with a site, as shown in Figure 5.

Figure 5 The Third Part of GetXmlFragment

if (!this.IsGlobal)
{
  foreach (PublishingPage publishingPage in publishingPageCollection)
  {
    if (publishingPage == defaultPublishingPage)
      continue;
 
    TaxonomyFieldValueCollection values =
      publishingPage.ListItem["Tags"] as TaxonomyFieldValueCollection;
                   
    foreach (TaxonomyFieldValue value in values)
    {
       if (value != null && value.TermGuid == term.Id.ToString())
       {
         url = publishingPage.Uri.AbsoluteUri;
         xml += "<Term Name='" + publishingPage.Title + "' URL='" +
           url + "?CurrentTermId=" + term.Id.ToString() + "'/>";
         break;
       }
     }
   }
 }

Figure 6 shows an example of a page based on the master page that uses TaxonomyDataSource.
An Example Page Based on a Master Page that Uses TaxonomyDataSource
Figure 6 An Example Page Based on a Master Page that Uses TaxonomyDataSource
Thanks to TaxonomyDataSource, the top and left navigation menus display different tiers of the portal taxonomy, which is maintained centrally in the term store.

The ListSiteMapPath Control

As mentioned, implementing the portal taxonomy in a term store instead of the portal site structure introduces two challenges. In the previous section I discussed the first challenge and provided a solution. This section discusses the second challenge and provides a solution to address it.

v4.master contains a control named PopoutMenu that renders a control named ListSiteMapPath. ListSiteMapPath renders the breadcrumb that shows up when the user clicks PopoutMenu, which is shown as an icon on the page.

ListSiteMapPath inherits from SiteMapPath, which is a traditional control for rendering the breadcrumb. Also, default.master uses SiteMapPath, whereas v4.master uses ListSiteMapPath.

ListSiteMapPath exposes a property named SiteMapProviders that takes a comma-separated list of site map provider names. As such, ListSiteMapPath can work with more than one provider. ListSiteMapPath iterates through these providers and takes the following steps for each enumerated provider. It first invokes the FindSiteMapNode method of the provider, passing in HttpContext to return the current site map node. It then recursively invokes the GetParentNode method of the provider to access all ancestor site map nodes of the current site map node all the way up to the root site map node. Finally, it renders an item in the breadcrumb for each ancestor site map node starting from the root site map node all the way down to the current site map node.

As you can see, ListSiteMapPath renders a complete hierarchy of items for each enumerated site map provider. This way, each site map provider contributes to the breadcrumb. This enables ListSiteMapPath to render site map nodes from different types of sources. In my case, I want to have ListSiteMapPath render site map nodes from the portal taxonomy maintained in the term store. I’ll implement a custom site map provider named TaxonomySiteMapProvider to achieve this.

TaxonomySiteMapProvider overrides FindSiteMapNode, where it uses the specified context to access the respective SPListItem. This context is basically the current context. The method then accesses the URL, ID and title of this list item and creates a site map node to represent it. Keep in mind that this list item in publishing sites represents a publishing page.

ListSiteMapPath invokes this method to access the current site map node:

SPContext spContext = SPContext.GetContext(context);
SPListItem listItem = spContext.ListItem;
string url = listItem.Url;
string key = listItem.ID.ToString();
string title = listItem.Title;
return new SiteMapNode(this, key, url, title);

ListSiteMapPath then invokes GetParentNode, passing in the current site map node to return the site map node that represents the parent of the current node. Keep in mind that the current site map node represents the current publishing page and the parent site map node represents the parent of this publishing page. The parent of a publishing page in this case is the publishing page that’s tagged with the parent term of the term with which the current publishing page is tagged.

The TaxonomyDataSource override of GetParentNode first accesses the current SharePoint list, which is nothing but the Pages document library that contains the publishing pages. It then creates a Collaborative Application Markup Language (CAML) query to search this list for the current publishing page so it can access the term with which the current page is tagged. This time around, I’m not using the publishing API to access this publishing page. Using a CAML query provides better performance, especially if the Pages document library contains tens of thousands of pages.

The following code section of GetParentNode basically returns the publishing page that the specified node represents, and then retrieves the value of the Tags field, which is the term with which the page is tagged:

SPQuery query = new SPQuery();
 
query.ViewFields = "<FieldRef Name='Tags'/>";
query.Query = "<Where><Eq><FieldRef Name='ID'/><Value Type='Integer'>" +
  node.Key + "</Value></Eq></Where>";
SPListItemCollection listItems = list.GetItems(query);
SPListItem listItem = listItems[0];
 
TaxonomyFieldValueCollection values =
  listItem["Tags"] as TaxonomyFieldValueCollection;
TaxonomyFieldValue value = values[0];
 
Guid termGuid = new Guid(value.TermGuid);
TaxonomySession taxonomySession = new TaxonomySession(context.Site);
Term term = taxonomySession.GetTerm(termGuid);

Next, GetParentNode accesses the parent term of the current term and searches the Pages document library for the publishing page that’s tagged with this parent term. It finally accesses the URL and ID of this publishing page and uses these two pieces of information, plus the name of the parent term, to form the parent site map node, as shown in Figure 7.

Figure 7 Forming the Parent Site Map Node

Term parentTerm = term.Parent;
if (parentTerm != null)
{
  int[] wssIds = TaxonomyField.GetWssIdsOfTerm(context.Site, parentTerm.TermStore.Id,
    parentTerm.TermSet.Id, parentTerm.Id, false, 500);
  query = new SPQuery(list.DefaultView);
  query.Query =
    "<Where><In><FieldRef LookupId='True' Name='Tags'/><Values>";
  foreach (int wssId in wssIds)
  {
    query.Query += ("<Value Type='Integer'>" + wssId.ToString() + "</Value>");
  }
  query.Query += "</Values></In></Where>";
    listItems = list.GetItems(query);
    listItem = listItems[0];
 
    string url = listItem.Url;
    string key = listItem.ID.ToString();
    string title = parentTerm.Name;
 
      return new SiteMapNode(this, key, url, title);
}

The code in Figure 7 uses the GetWssIdsOfTerm static method of the TaxonomyField class. This method returns the Windows SharePoint Services (WSS) IDs of the specified term from the taxonomy hidden list, which is a SharePoint list at the site-collection level where SharePoint caches terms used in the site collection. Every time you tag an item with a new term, SharePoint automatically adds an entry to this hidden list for that term. The same term may have multiple entries in this taxonomy hidden list if, for example, the term is reused.

GetWssIdsOfTerm returns the IDs of all list items that represent the specified term in the taxonomy hidden list. You have to use these IDs in the CAML queries that filter based on managed metadata fields, which is the Tags field in this case.

Figure 8 shows the ListSiteMapPath that’s bound to the TaxonomySiteMapProvider. Note that the control renders the entire hierarchy to which the current page belongs.

The ListSiteMapPath that Is Bound to the TaxonomySiteMapProvider
Figure 8 The ListSiteMapPath that Is Bound to the TaxonomySiteMapProvider

Building Information Architecture for Knowledge-Management Portals

As discussed, a traditional guided-navigation approach takes users through a set of menu options to help them find the content they’re looking for. This is quite different from social networking portals, where approaches such as the following are used to help users find content:

  • Search engine: Users perform search queries against search indices to find content.
  • Metadata filtering: Users use metadata filtering to filter search results.
  • Wiki-style links: Pages are linked together through wiki-style links. Users use these links to navigate between pages.
  • Rollup Web Parts: Pages contain Rollup Web Parts that roll up content from various places. Users use the links in these Web Parts to navigate between pages.

Social networking portals promote the free-flow approach to navigation as opposed to the linear guided approach used in traditional portals. Thus, users can arrive at a page through different means. In the following sections, I’ll discuss the design and implementation of a knowledge-management portal that takes advantage of the social networking navigation model. Sean Squires and Lincoln DeMaris presented this design during a session at the Microsoft SharePoint Conference 2009. I’ll cover some aspects of this design and also present my own customizations and enhancements to it. The next section discusses the homepage of this knowledge-management portal.

The Homepage

The main goal of the homepage is to provide the following two capabilities: search box and Rollup Web Parts. The search box enables users to search for content. This is the main component of the homepage. The Rollup Web Parts roll up content from various places throughout the enterprise.

This content could be anything that’s of some interest to users. For example, it may be beneficial to users to show them the most-viewed and most-searched content in the enterprise. SharePoint 2010 comes with an out-of-the-box Web Analytics Web Part that you can configure to display such content.

A knowledge-management portal normally stores documents in a central repository. SharePoint comes with an out-of-the-box site template named Document Center. You can create a site from this site template to store your documents. You can then add a content query Web Part to the homepage to roll up the most recently uploaded documents.

When the user uses the search box shown in the homepage to perform a search query, she’s taken to the search results page. This page is the standard SharePoint out-of-the-box search results page that comes with a refinement panel. This panel contains categories that the user can use to filter search results. One of these categories is result types.

The user selects the result type associated with Web pages to view Web pages only. The refinement panel also presents the terms with which these Web pages are tagged. These are the terms from the portal taxonomy. The user uses these terms to further filter these pages to arrive at the desired page. The user then clicks on the search result to navigate to the page, which is basically an enterprise wiki page.

Structure of an Enterprise Wiki Page

All pages in my knowledge-management portal are created from the same customized version of the enterprise wiki page layout.

To customize the page layout, I start by replacing the Wiki Categories field control with the Tags field control. I’ll also place a Tags field control inside an EditModePanel so that it only shows up when the page is in Edit mode. The reason I don’t want the Tags field control to show up in the Display mode is because I’ll use the left navigation menu to render the terms with which the current page is tagged instead of the Tags field control. As you’ll see shortly, using AspMenu will allow me to not only render the current terms, but also the current terms’ subterms, sub-subterms and so on.

I also need to make changes in the implementation of TaxonomyDataSource so the control doesn’t use the query string to get the current term. Instead, it should retrieve the current terms from the Tags field of the current page. This is because I’m no longer taking the user through the top navigation menu, which is how it’s done in a guided-navigation model used in traditional portals. The user can access the current page from anywhere. For example, there could be a wiki link on some page that takes the user to the current page. As such, there’s no query string parameter that would tell you about the current term. This also means that the current term may no longer be a single term, because the current page may have been tagged with multiple terms.

The new version of TaxonomyDataSource first accesses all terms with which the current page is tagged. It then iterates through these terms and creates XML fragments for these terms and their descendant terms, as shown in Figure 9.

Figure 9 The New Version of TaxonomyDataSource

TaxonomyFieldValueCollection values =
  SPContext.Current.ListItem["Tags"] as TaxonomyFieldValueCollection;
Term currentTerm = null;
foreach (TaxonomyFieldValue value in values)
{
  currentTerm = taxonomySession.GetTerm(new Guid(value.TermGuid));
  GetXmlFragment(publishingPageCollection, currentTerm, ref xml);
  termsToReturn = currentTerm.Terms;
 
  if (termsToReturn != null)
  {
    foreach (Term term in termsToReturn)
    {
      GetXmlFragment(publishingPageCollection, term, ref xml);
    }
  }
}

Figure 10 shows a page based on my customized version of an enterprise wiki page layout.

A Page Based on a Customized Enterprise Wiki Layout
Figure 10 A Page Based on a Customized Enterprise Wiki Layout

Note that Quick Launch displays the current terms, the subterms of the current terms and the pages that are tagged with the current terms and subterms of the current terms. Also note that the page doesn’t display the Tags field control in the Display mode.

Figure 11 shows the page in the Edit mode where the Tags field is shown, allowing the content author to tag or retag the page.

A Page in Edit Mode, Allowing Tagging or Retagging
Figure 11 A Page in Edit Mode, Allowing Tagging or Retagging

Thanks to TaxonomyDataSource, the left navigation menu exposes links to all pages that are related to the current page. It exposes links not only to the pages that are tagged with the same terms as the current page, but also to pages that are tagged with the descendant terms of these terms. You can enhance TaxonomyDataSource to give the content author the option of specifying whether to display only the child terms of the terms with which the current page is tagged, the child terms and the grandchild terms, or the child terms, grandchild terms and the grand-grandchild terms, and so on. You can also give the author the option to display the parent terms of the terms with which the current page is tagged, the parent terms and the grandparent terms, and so on.

Thus, TaxonomyDataSource becomes a powerful tool that ensures when a user visits a page, the page presents the user with links to all related pages throughout the enterprise. As more pages are added and tagged with the same terms with which the current page is tagged or with the descendant terms of the same terms, TaxonomyDataSource automatically picks them up, so Quick Launch is automatically updated to include links to these new pages. TaxonomyDataSource makes it possible to use the portal taxonomy as the common thread that chains together all pages and documents throughout your enterprise.

Links on the Quick Launch are just one way to make connections between the pages of your portal. Another way is to use wiki-style page linking. Yet another means is to use Rollup Web Parts that bring in related content from throughout the enterprise. For example, you can configure a Content Query Web Part to pull documents from your document center and filter them based on the terms with which the current page is tagged. As users upload new documents to the document center and tag them with the same terms with which the current page is tagged, this Content Query Web Part automatically updates to show these new documents as well.

As the content author adds a new tag to the page, edits the existing tag or removes an existing tag, the content of all Rollup Web Parts on the page and the content of Quick Launch automatically changes. This is an example of contextual social networking, where the page displays content based on the taxonomy.

TaxonomyDataSource, wiki-style linking and Rollup Web Parts turn a page into a forefront for pulling related content from throughout the enterprise to one location, filtered based on the terms with which the page is tagged. Pages are no longer explicitly structured through physical structure of the portal. Instead, they’re implicitly structured via the relationships they have through taxonomy, wiki-style linking and Rollup Web Parts.

Flexible and Customized Information Architecture

Wrapping up, the new SharePoint 2010 ECM capabilities help you build and implement flexible information architecture for Internet/intranet/extranet-facing publishing and knowledge-management portals. This flexibility is becoming more important as Web page and site design evolve to accommodate new ways of using the Internet, as exemplified by the explosion in social networking sites. While I presented the design and implementation of several custom SharePoint components that can be used to implement information architecture, there’s much more that can be done, and I urge you to explore the new possibilities.


Shahram Khosravi specializes in and has extensive industry experience with SharePoint architecture, design and development. He’s the author of the following books: “Expert WSS 3.0 and MOSS 2007 Programming” (Wrox, 2008), “Professional SharePoint 2007 Workflow Programming” (Wrox, 2008), “ASP.NET AJAX Programmer’s Reference” (Wrox, 2007), “ASP.NET 2.0 Server Control and Component Development” (Wrox, 2006) and “IIS 7 and ASP.NET Integrated Programming” (Wrox, 2007).