Category: Top Posts

User Profiles – Adding a Custom Tab and Page

The User Profile Pages within the MySites area of SharePoint 2010 is significantly more robust than in previous versions.  I really like the concept of making it a central/social hub and using it as a central place to hold and maintain content.  While I generally shy away from major customizations to the Personal sites (aka My Content), the My Site Host that stores the User Profile Pages and Activity Stream provides a great place to load centralized content and features.  It could be for integrating into other social applications your organization might use, or perhaps a place to hold executive Dashboards.  Since the My Site Host is shared between all users within that profile store it is easier to maintain than the individual Personal sites.

I have found myself in a number of conversations recently with people asking about how to customize and extend the User Profile Pages but I have not seen any good sources of information on how to approach it.  Today we will look at how you can define a custom ApplicationPage that uses the User Profile format and how to link to it from within the tabs present in the User Profiles section.

Managing User Profile Tabs

For those that are not aware, the horizontal tabbed navigation displayed in the User Profiles section is actually powered by the QuickLaunch menu for the MySite Host site.  You can add a new tab simply by adding a new Heading entry for the QuickLaunch.

Here is a screenshot of the tabbed QuickLaunch on the User Profile page.

Here is what the QuickLaunch configuration looks like:

 

Custom User Profile ApplicationPage

The sample code included in this project includes the standard formatting for the page so that it blends in perfectly with the other pages within the section.  Your custom page has access to the full Server OM and can include complex user controls or visualizations.

<%@ Page language="C#" DynamicMasterPageFile="~masterurl/custom.master" Inherits="Microsoft.SharePoint.Portal.WebControls.PersonalAdminPage,Microsoft.SharePoint.Portal,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="OSRVWC" Namespace="Microsoft.Office.Server.WebControls" Assembly="Microsoft.Office.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="OSRVUPWC" Namespace="Microsoft.Office.Server.WebControls" Assembly="Microsoft.Office.Server.UserProfiles, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SPSWC" Namespace="Microsoft.SharePoint.Portal.WebControls" Assembly="Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SEARCHWC" Namespace="Microsoft.Office.Server.Search.WebControls" Assembly="Microsoft.Office.Server.Search, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<asp:Content ID="Content1" ContentPlaceHolderID="PlaceHolderLeftNavBarDataSource" runat="server">
<SPSWC:DataSourceReplacer runat="server" ControlID="PlaceHolderLeftNavBar/QuickLaunchNavigationManager/QuickLaunchMenu">
<SPSWC:MySiteDataSource runat="server" ShowStartingNode="False" SiteMapProvider="MySiteLeftNavProvider" />
</SPSWC:DataSourceReplacer>
</asp:Content>

<asp:Content ID="Content2" contentplaceholderid="PlaceHolderPageTitle" runat="server">
Custom Page Title
</asp:Content>

<asp:Content ID="Content3" contentplaceholderid="PlaceHolderPageTitleInTitleArea" runat="server">
Custom Page Title
</asp:Content>

<asp:Content ID="Content4" contentplaceholderid="PlaceHolderSiteName" runat="server">
<SPSWC:PersonalSpaceMainHeading TitleMode="true" runat="server" />
</asp:Content>

<asp:Content ID="Content5" contentplaceholderid="PlaceHolderMain" runat="server">
<Sharepoint:FormDigest runat="server" id="FormDigest" />

<div class="s4-notdlg noindex">
<a id="A1" href="javascript:;" onclick="javascript:this.href='#SkipContactCard';" class="ms-SkiptoMainContent" runat="server">
<SPSWC:StringValueEx ResourceFile="spscore" LocId="SkipContactCard_Text" runat="server" />
</a>
</div>
<SPSWC:ProfilePropertyLoader id="m_objLoader" LoadFullProfileOfCurrentUser="true" runat="server" />
<div class="ms-profilepageheader">
<table border="0" cellpadding="3" cellspacing="0" ID="ZoneTable" width="100%" class="ms-tztable">
<!-- business card zone -->
<tr><td colspan=3 width="100%" style="padding:24px">
<table class="ms-contactcardtext3" cellspacing=0 cellpadding=0 width="100%">
<tr>
<td width="150" valign="top">
<div class="ms-contactcardtext3" style=""width:100%;">
<SPSWC:LocalTimeControl runat="server" />
</div>
<div class="ms-profilepicture ms-contactcardpicture ms-largethumbnailimage">
<SPSWC:ProfilePropertyImage PropertyName="PictureUrl" RenderWrapTable="False" ShowPlaceholder="true" id="PictureUrlImage" ImageSize="Large" CenterVertically="true" runat="server"/>
</div>
<div class="ms-contactcardtext3" style="width:100%;">
<div><SPSWC:EditProfileButton runat="server" id="ddlEditProfile" /></div>
<div><SPSWC:AddToColleaguesButton runat="server" id="TBBAddtoColleagues" class="ms-my-addcolleague"/></div>
</div>
</td>
<td valign="top" width="320">
<div style="min-height:175px">
<div class="ms-contactcardtext2 ms-identitypiecenotediv">
<SPSWC:StatusNotesControl runat="server"/>
</div>
<div style="padding-top: 50px;vertical-align:top;" class="ms-name ms-contactcardtext1" id="ProfileViewer_Name">
<SPSWC:PersonalSpaceMainHeading TitleMode="false" runat="server"/>
</div>
<div style="padding-left: 20px;" class="ms-contactcardtext3" id="ProfileViewer_ValueTitle">
<SPSWC:ProfilePropertyValue PropertyName="Title" runat="server"/>
</div>
<div style="padding-left: 20px;" class="ms-contactcardtext3" id="ProfileViewer_ValueDept">
<SPSWC:ProfilePropertyValue PropertyName="Department" runat="server"/>
</div>
<br/>
<div style="padding-left: 20px;" class="ms-contactcardtext3" id="ProfileViewer_ValueOfficePhone">
<SPSWC:ProfilePropertyValue PropertyName="WorkPhone" dir="ltr" runat="server"/>
</div>
<div style="padding-left: 20px;" class="ms-contactcardtext3" id="ProfileViewer_ValueOfficeLocation">
<SPSWC:ProfilePropertyValue PropertyName="Office" runat="server"/>
<SPSWC:ProfilePropertyCheckValue PropertyNames="Office,Location" runat="server" Text=", "/>
<SPSWC:ProfilePropertyValue PropertyName="SPS-Location" runat="server"/>
</div>
<div style="padding-left: 20px;" class="ms-contactcardtext3" id="ProfileViewer_ValueEmail">
<SPSWC:ProfilePropertyValue PropertyName="WorkEmail" runat="server" PrefixBrIfNotEmpty="true"/>
</div>
<br/>
<div style="padding-left: 20px;display:none;" class="ms-contactcardtext3" id="ProfileViewer_ProfileDetails" >
<SPSWC:ProfileDetailsViewer id="ProfileViewer" Collapsible="false" runat="server" />
</div>
</div>
<div style="padding-left: 20px;" class="ms-contactcardtext3">
<SPSWC:ContactCardDetailsLink AboutMeId="ProfileViewer_ValueAboutMe" ProfileDetailsId="ProfileViewer_ProfileDetails" EllipsisId="ProfileViewer_Ellipsis" runat="server" />
</div>
</td>
<td valign="top" width="418">
<div style="padding: 50px 5px 0px 15px; overflow:hidden; min-height: 142px; height: 142px;" class="ms-contactcardtext3" id="ProfileViewer_ValueAboutMe">
<SPSWC:ProfilePropertyValue PropertyName="AboutMe" runat="server" PrefixBrIfNotEmpty="false"/>
</div>
<div style="padding-left: 15px;display:none" id="ProfileViewer_Ellipsis" class="ms-contactcardtext3"></div>
</td>
</tr>
</table>
</td></tr>
</table>

<SharePoint:AspMenu
ID="MySiteSubNavigationMenu"
Runat="server"
EnableViewState="false"
DataSourceID="MySiteSubNavDS"
AccessKey="<%$Resources:wss,navigation_accesskey%>"
UseSimpleRendering="true"
UseSeparateCss="false"
Orientation="Horizontal"
StaticDisplayLevels="1"
MaximumDynamicDisplayLevels="0"
PopOutImageUrl=""
SkipLinkText=""
CssClass="s4-sn">
</SharePoint:AspMenu>
<SPSWC:MySiteDataSource
ShowStartingNode="False"
SiteMapProvider="MySiteSubNavProvider"
id="MySiteSubNavDS"
runat="server"/>

</div>

<a name="SkipContactCard" tabindex=0></a>

<div class="ms-profilepagecontent">
Your Page Content Here!
</div>
</asp:Content>

Managing the QuickLaunch Entries

For this to be a robust feature the QuickLaunch entries should be managed as part of the feature activation/deactivation process so a FeatureReceiver has been added to the project to add and remove the entries respectively.

using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Navigation;
using Microsoft.SharePoint.Security;

namespace CustomMySitePage.Features.Customer_User_Profile_Page
{
[Guid("d2754488-4038-42fd-9c9e-f93f19d4e093")]
public class Customer_User_Profile_PageEventReceiver : SPFeatureReceiver
{
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
using (SPWeb web = properties.Feature.Parent as SPWeb)
{
SPNavigationNode node = new SPNavigationNode("Custom Page", web.Url + "/_layouts/CustomMySitePage/PageTemplate.aspx");
web.Navigation.QuickLaunch.AddAsLast(node);
}
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
using (SPWeb web = properties.Feature.Parent as SPWeb)
{
SPNavigationNodeCollection nodeCol = web.Navigation.QuickLaunch;
foreach (SPNavigationNode node in nodeCol)
{
if (node.Title == "Custom Page")
{
node.Delete();
}
}
}
}
}
}

Deployed Custom Tab and Page

Here is a final look at what it looks like with the feature activated and the Tab and Page available.

 

Template Project Source Code

The source code for this project is available for download here:  CustomMySitePage Project

I will not be providing support for the code as it is merely an example of how the pages within the User Profile section of MySites works.

Use the Status Bar to Display Active Workflows

Over the past six months I’ve found myself moving more and more towards using the new Client OM for things that I used to use jQuery and SPServices for.  While I still love SPServices, it is not always required and now that I am getting more knowledgeable about how to do things in the Client OM, things are getting a lot easier (although you have to go back to writing CAML).

After a recent session at SharePoint Saturday The Conference an attendee asked me about ways to show a user they have an open workflow task.  After thinking about it, it was pretty similar to something else I had done recently that involved reading for a SharePoint list and adding an item to the status bar using the Client OM.  This particular request involved a little more code in order to properly filter down the task list, but it works like a charm.  It is important to remember that the client OM only works within the given Site Collection, and this particular code as-is will only look at the site the user is on at the time.

If you want this to display on a single page, you can simply add it to the page. In order to have it show up throughout the site though, you will want to add it to the MasterPage. If the MasterPage is used throughout many sites and or site collections, it will show any open tasks for the specific site.

To start off, we will create a div container. I typically put something like this just inside the tag of the MasterPage. Add in some global variables and then add in the Ajax Execute/Delay command that will call the first method, getUser() when the page is fully loaded.

   <div id="SetStatus"><script type="text/ecmascript" language="ecmascript">
    var url = '';
    var statusId = '';
    var isitDlg = window.location.href.match(/isDlg/i) != null;
    var taskOutput;
    var context = null;
    var web = null;
    var curUser = null;

    if (!isitDlg) {
        ExecuteOrDelayUntilScriptLoaded(getUser, "sp.js");
    }
</script></div>[/sourcecode]   

Next, within the script block we will add the methods to support getUser(). These methods will get the current user's information which is needed to be able to load only the current user's assigned tasks.

[sourcecode language="js"] function getUser() { context = new SP.ClientContext.get_current(); web = context.get_web(); curUser = web.get_currentUser(); curUser.retrieve(); context.load(web); context.executeQueryAsync(Function.createDelegate(this, this.onSuccessMethod), Function.createDelegate(this, this.onFailureMethod)); } function onSuccessMethod(sender, args) { var user = web.get_currentUser(); LoadNotifications(); } function onFailureMethod(sender, args) { alert('Error: ' + args.get_message() + 'n' + args.get_stackTrace()); }

With the user’s name available we can now read the list items from the local Task List that are active and assigned to the user. If successfull the (ReadListItemSucceeded() method is called otherwise if an error is raised the ReadListItemFailed() is called.

function LoadNotifications() {  	
var listTitle = "Tasks";  	
context = SP.ClientContext.get_current();  	
var list = context.get_web().get_lists().getByTitle(listTitle);  	
var camlQuery = new SP.CamlQuery();  	
camlQuery.set_viewXml("<view><query><viewfields><fieldref name="ID" /><fieldref name="Title" /><fieldref name="FileDirRef" /><fieldref name="StartDate" /><fieldref name="DueDate" /></viewfields><where><and><eq><fieldref name="AssignedTo" /><value type="User">" + curUser.get_title() + "</value></eq><or><eq><fieldref name="Status" /><value type="Choice">In Progress</value></eq><eq><fieldref name="Status" /><value type="Choice">Not Started</value></eq></or></and></where></query></view>");  	
this.listItems = list.getItems(camlQuery);  	
context.load(listItems);  	
context.executeQueryAsync(ReadListItemSucceeded, ReadListItemFailed);  
}  

The returned list of tasks is now processed and the output is prepared. If tasks are prepared, the SetStatus() method is called to show the status bar message.

function ReadListItemSucceeded(sender, args) {  	
var itemCount = 0;  	
taskOutput = " <div id="tasks"> <table width="800"> <tbody> <tr style="font-weight: bold"> <td width="500">Task</td> <td width="150">Start Date</td> <td width="150">Due Date</td></tr>";  	var items = listItems.getEnumerator();          
while (items.moveNext()) {             
itemCount += 1;             
var listItem = items.get_current();             
var isLate = false;             
taskOutput += "<tr><td><a href="&quot; + listItem.get_item(" FileDirRef') + "/DispForm.aspx?ID=" + listItem.get_item('ID') + "' target='_top'>" + listItem.get_item('Title') + "</a></td><td>" + listItem.get_item('StartDate') + "</td><td>" + listItem.get_item('DueDate') + "</td></tr>";         
}         
taskOutput += "</table></div>";         
if (itemCount > 0) {             
SetStatus("Reminder: ", "You have a workflow task! <a href="javascript:ShowTasks()">View open Workflow Tasks</a>", isLate);         
} 
} 
function ReadListItemFailed(sender, args) {         
alert('Error: ' + args.get_message() + 'n' + args.get_stackTrace()); 
} 

The SetStatus() method shows how simple it is to work with the Client OM. The SP.UI.Status.addStatus call will post the message including the title and the body of the message. We then make a second call to SP.UI.Status.setStatusPriColor to change the color of the bar. Pretty simple, yet so powerfull!

function SetStatus(title, message, isLate) { 	
statusId = SP.UI.Status.addStatus(title, message, false);         
if (isLate) {             
SP.UI.Status.setStatusPriColor(statusId, 'red');         
}         
else {             
SP.UI.Status.setStatusPriColor(statusId, 'yellow');         }     } 

The last method is the ShowTasks method which is the javascript function that is called from within the status messages to show a simple output inside of the Client OM’s ModalDialog. First format the display options for the window and then call the SP.UI.ModalDialog.showModalDialot() method.

function ShowTasks() {  	
var _html = document.createElement('div');  	
_html.innerHTML = taskOutput;  	
var options = { html: _html, autoSize:true, allowMaximize:true, title: 'Open Workflow Tasks', showClose: true };  	
var dialog = SP.UI.ModalDialog.showModalDialog(options);  
}  

Not a lot of code to provide a very usefull solution that does not require anything to be deployed to the server. Below are screenshots of the solution in action.

Open Workflow Tasks Status

Open Workflow Tasks Listing

There are definitely some things that can be done to better format the output of the ModalDialog, but you get the idea. This is just one way to leverage the Client OM for some very useful business features.

Quota Management and Storage Reports in SharePoint 2010

A few years ago I wrote an article about how to enable and work with the Quota Management features in SharePoint 2007 (click here for article) which proved to be a popular post.  Quota Management is a pretty important topic when it comes to SharePoint Governance and overall maintenance of the platform.  While the overall Quota Management features in SharePoint 2010 were maintained, there was one big feature left out when SharePoint first shipped, and that was the “Storage space allocation” page also known as StoreMon.aspx page that was available to Site Collection administrators from the Site Settings page.  

New Storage Metrics

With the release of SharePoint 2010 SP1 (download here) the feature returns, but in a much different format and vastly improved.  The page was renamed “Storage Metrics” and it is a gold mine of information since it provides a way for Administrators to navigate through the content locations on the site and provides details for the item’s Total Size, % of Parent, % of Site Quota, and Last Modified Date.  This makes it easy for administrators to identify where content is concentrated, and can also show an exceptionally large lists, libraries, folders, and documents. 

There was one aspect of this that I thought was helpful in 2007 that is no longer supported, and that is the ability to view the number of versions of a given document right from the report.  In many cases I’ve seen versioning turned without any limits, and some popular documents might have 1,000s of versions.  The report used to provide a way to find those exceptions so that they could be cleaned up.

 

Performance Improvements

From what I understand, it was removed because it proved to be extremely resource intensive and information was gathered in real-time so it could cause service stability issues in very large environments.  With its return is a completely revamped gathering process that relies on timer jobs, titled Storage Metrics Processing, resulting in much faster page loads and no risk of crashing the server just by viewing the report.  These jobs will pull data every 5 minutes but like all timer jobs, the frequency can be adjusted to better meet your needs and environment.  For larger environments, it might be a good idea to reduce that frequency to avoid the extra overhead.

Configuring Quotas

As with the 2007 version, this feature is only available if quotas are enabled.  In cases where quotas are not currently being used and proper limits managed, the safest bet is to establish a quota that cannot be met.  This will enable the features without the risk of triggering a warning or locking a site that exceeds the thresholds.  Locking the site is the only risk with quotas, there is no risk of data loss.

Summary

Both Farm and Site Collection Administrators should review the functionality and add its review into their content review and cleanup processes.

Disabling the “What’s Happening?” Display on the User Profiles

One of the SharePoint 2010 features I really like is the user status information that is integrated into the updated User Profiles.  Unfortunately some organizations are not quite comfortable or ready to support the feature.
Here is what it looks like with the user status set:
User Profile with Status

Disabling the Status Message field

With normal User Profile property management you can disable the Status Message field (known internally as SPS-StatusNotes).  You want to not only change the Edit Settings to not allow users to edit the value, but you want to also ensure that the property is not displayed on any of the profile pages.
Here is a screenshot of the Edit User Profile Property screen:
Edit User Profile Property screen

Removing the Control

While the property is now disabled and we have configured it to not show on the regular profile forms, the Status Message control still shows on the Person.aspx page and gives the users the impression that they can continue to use it.
Here is a screenshot from after the property was disabled:
Edit User Profile without Status
The status essentially stays unset, and anything that the user might type in cannot be saved. There is no way for them to know that the status can no longer be set and no error is displayed so it is confusing.
The control cannot be removed through configuration unfortunately.  To remove the control you will need to edit the Person.aspx page in the MySite Host site collection.  It is important to note that the Person.aspx page is shared among all user profiles in the User Profile Service Application so any changes to the page impact all uses.
To edit the page you will need to be a site collection administrator for the site collection.  Open the site collection in SharePoint Designer and then open the Person.aspx page.  You will need to go into Advanced Mode, which is selectable in the SharePoint Designer ribbon on the right hand side.
Once the page is editable, find and remove the following code:

[sourcecode type=”plain”]
<SPSWC:StatusNotesControl runat="server"/>
[/sourcecode]

Once the page is saved, the control will be removed and you will no longer see the pop-up bubble.
Here is what it looks like with the control removed:
Edit User Profile without Status Control

Re-Enabling the Control In The Future

If you need to re-enable the control in the future, you can simply reset it to the original Site Definition in SharePoint Designer.  This will restore the control and the page back to normal.

User Profiles – Creating Custom Properties

The User Profiles in SharePoint Server represent a very robust and flexible way to manage information about the members of your organization.  It can be used to fill the roll of a searchable Employee Directory, used to drive business processes and workflows, and also makes it easier to find people in the organization based on their expertise and user property attributes providing social networking functionality. 

The default properties that are created at the time of installation are just a starting point.  In this article I will show you just how easy it is to create new properties that help support your organization and business processes. 

Planning The New Property

When defining new fields here is a selection of things to consider:

  • Name / Display Name
  • Type – Wide range of field types
  • Length – Cannot be modified in some situations
  • Configure a Term Store Set – Managed Meta Data
  • Policy Setting – Required, Optional, or Disabled
  • Privacy Settings – Field level privacy
  • Edit Settings – User maintained or administrator/system maintained
  • Display Settings – Show on View/Edit/Newsfeed
  • Search Settings – Support for a user Alias (i.e. Employee ID) and if it is Indexed
  • Profile Synchronization – You also have the ability to configure a synchronization with an external system (i.e. CRM, HRIS)

In many cases the options change based on the value of previous options.  A good example is based on the settings with the Type of string (Multi Value) or the Policy Setting.

Create Custom Property Walkthrough

Since I work in consulting, much of our content is very much client focused.  This is a great example of a property that would be very important to us, but not so important for the average company.  In my case, I want to allow consultants to add one or more customer names that they have worked with.  Since this valuable information could potentially be used for a number of purposes, (like tagging) throughout the entire SharePoint environment, I have decided to create a Managed Meta Data Term Set for this property so that we can reuse the content. 

Here is a quick shot of the Client List I created in the Term Store.

Define A Term Set

To create a new property, browse out to the User Profiles Service Application (or whatever your Profile Service App is named) and select the Manage User Properties link.

Manage User Properties

A full listing of the User Properties is displayed with properties organized into sections.  They can be ordered and placed into sections as needed.  To create a new property, simply click the New Property menu item.

New Property

Complete the main Property Settings.  In many cases changes to these settings cannot be made which means the previous property would have to be deleted and recreated.  In this case I created my Clients property and set it to a multi-value string separated by semicolons.  I then pointed it to the Client List Term Set previously configured.

Property Definition

The next set of fields control how the list is displayed and if it can be edited.  In this case, I want to make it an optional property and encourage consultants to maintain the value so I will enable it in each of the Display Settings.  It is not confidential information, so I will be sure to set the Privacy level to Everyone. 

Display and Policy Settings

Here is what the current profile looks like when rendered.  You can see that the Clients field is displayed and each value a link that feeds into the People Search.

Profile View

Once the values have been crawled and are available in the search index, you will start to see results in the people search process. 

People Search

Summary

By extending the User Profiles with custom properties you can leverage the robust platform to support an organization and its unique processes and content.

Site Topology Planning and Taxonomies

In the previous article SharePoint Site Topology Planning I discussed some of the technical implications of organizing the the sites within one or more applications, site collections, and sub-sites.  The article started to get pretty long so I decided to save the taxonomy part of the discussion for a separate article.

Organizing Sites

In the previous article I addressed different types of content and using that to help segment the sites across applications and site collections.  Within a given application it is possible to provide some meaningful segmentation by configuring managed paths. 

For example, you may segment collaboration sites with the following url structure:

  • http://collaborate.company.com – Collaboration Application
    • /Communities – Communities of Practice Managed Path
      • /Proposals – Proposal Community of Practice Site Collection
      • /Procurement -  Procurement Community of Practice Site Collection
    • /Projects – Projects Managed Path
      • /Alpha – Alpha Project Site Collection
      • /Omega – Omega Project Site Collection

Organizational Hierarchies

Most people understand hierarchies, and most businesses (at least in the west) have been organized in hierarchies for many years.  It is natural for people to think of their organization in this manner, but this may not be the best way to plan for the topology of your sites.

Traditional Intranets tend to go from the largest organizational unit down to the smallest.  There may be multiple divisions, with multiple business units, with multiple departments, with multiple teams, with people that actually do the work.  Sites or portals that go 5 or more levels deep can become very difficult to manage and even harder to use.  Modern businesses need to remain agile with teams always being redefined, combined and split up.  In  most situations it is a good idea to fight the hierarchy tendencies and strive for a flatter structure. 

From a SharePoint perspective a flatter structure with more site collections will make it easier to reorganize sites and structure versus a single site collection with 5+ sub-site layers deep.  As previously discussed Site Collections can be backed up and restored with a high level of fidelity (completeness) compared to a sub-site’s export and import options.  The key to usability and manageability is to find the right amount of segmentation and site collection structure.

Finding Sites and Content in a Flat World

An alternative to a rigid hierarchy is adopting flexible taxonomies with tagging.  Tagging provides a flexible and dynamic method of describing the content and sites that can evolve over time.  A great example of this is a site like StackOverflow compared with the rigid structure of the MSDN/TechNet Forums. The flat structure decreases the chance of duplication and provides new opportunities to view the data in new and unique ways. 

The SharePoint 2010 system full supports tagging without the need for custom or third party add-on components.  I fully expect that these will be a popular feature within the new version. 

Summary

Following the guidance between the two articles you should be able to properly plan your site topology.  Assumptions and business decisions do change, but if you establish the right level of granularity with applications and site collections you will be able to migrate and relocate things as needed.

Related Posts

SharePoint Site Topology Planning

This is the next in a series of articles addressing core SharePoint implementation topics.  I hope that this is valuable to both groups looking to implement SharePoint for the first time as well as groups planning planning for an upgrade or migration to SharePoint 2010.

A Series of Containers

I tend to think about SharePoint from a container perspective.  Each of the containers has a set of settings and features that can be configured and administered for all of the containers within.  It is important to understand the boundaries of each level so that you can create a site topology that meets all of your objectives.  I’ll cover a sub-set of these I typically consider while planning the site topology.  The containers I’m going to address include:

Farm – In the context of this article, all of the Applications, Site Collections, and Sites hosted by one or more connected SharePoint servers.  I say “connected” because it is possible, and fairly likely that most organizations will have more than one Farm (Dev, Staging, Production for things like Intranet, Extranet, Internet, etc).  A farm has one or more content Applications plus additional applications for things like Central Administration.

Application – An application would be the top level address which maps to an application in IIS.  For example http://sharepoint.  An application has one or more Site Collections.  The application level is also the level where you have the opportunity to identify one or more content databases for storing the content on the SQL server.

Site Collection – A site collection has one or more Sites also referred to as Webs or Sub-Sites.

Sub-Sites or Webs – This is the smallest container which resides under and can be managed by the Site Collection.

Types of Content

The first step is identify what types of content or site(s) you expect to host.

  • Internet
  • Extranet
  • Intranet
  • MySites
  • Company or Divisional Portal(s)
  • Web Content Management (Publishing)
  • Electronic Content Management (Document Management)
  • Business Intelligence
  • Project Management
  • Team Collaboration
  • Application Hosting

Not all of those types of content apply to every organization, but it should be pretty clear that the content, the update frequency and the manner in which it is used and managed can vary quite a bit from type to type. 

Considerations of Multiple Applications

Very small companies or workgroups may be able to get by with all of the types of content hosted in a single Application, but for anything larger there should be plans to segment it to multiple Applications in the farm.

Here are some considerations when using multiple Applications:

Authentication Model – What type of authorization model will be used?  Options include Anonymous, Windows Integration (NTLM or Kerberos), and Forms Based Authentication (FBA).  Since an Application can only support a single authorization mode, that can dictate additional applications.

Edit:    Anders Rask was kind enough to point out some changes to the authorization model in 2010 that make the previous statements incorrect.

In 2007:  Options include Anonymous, Windows Integration (NTLM or Kerberos), and Forms Based Authentication (FBA).  Since an Application can only support a single authorization mode, that can dictate additional applications.

In 2010: There are two high level options; Classic which supports Windows Authentication only and Claims Based which supports one or more different providers.  Additional information can be found on TechNet’s article Plan Authentication Methods.

Sessions – When a user visits an application a session is created.  It is important to understand that their session is for a specific IIS Application so if they visit the main company portal and then click to see the MySite they may be asked to authenticate again.  With Anonymous this should not be an issue.  With Windows Integrated this should not be an issue if 1) users are using Internet Explorer 2) they access the site(s) with the same account they use to access their computer and 3) their browser is properly configured to pass logged on user information. 

Application Scoped Solutions – Solutions can be scoped to specific Applications which can provide some flexibility in deploying new features.  In a large environment it is important to only show features to the areas where they apply.

Considerations for Site Collections

The site collection has some important boundaries to consider when deciding to use one versus multiple site collections.  They include:

Amount of Data – It is important to keep your site collections at a maintainable level.  There is no hard limit, but as Site Collections grow over 40GB they can get more difficult to maintain and will take longer to restore.  SQL Server tuning becomes more critical with larger databases.  It is a good idea to segment your content across multiple site collections (and across multiple content databases) in a manner that makes sense.

SharePoint Groups – SharePoint Groups can be a good way to organize users, especially when they do not map to functional areas or groups otherwise managed in Active Directory.  These groups are defined and contained within a given Site Collection which means if you want to use them in multiple site collections they have to be duplicated and maintained separately which can be problematic.

Site Collection Administrator – A site collection administrator has great power and control within the site collection container.  A Site Collection administrator can choose themes, manage all security within the site collection, manage activated solutions and potentially deploy other customizations if policy permits.  Enabling content owners should be a top priority, and the Site Collection provides a good container for that.

Quota Management – Quotas are set at the Site Collection level so if granular quota management is required for billing or charge back purposes it may have some impact on how site collections are segmented. 

Navigation – The default SharePoint navigation provider does not span site collections (and therefore applications) which can make a standardized or unified navigation scheme difficult to maintain.  This tends to work fine for team collaboration sites, but can be cumbersome when you need to link many site collections.

Content Types – I think Content Types were one of the most important changes introduced with WSS 3.0 and MOSS.  An entire overview of content types is outside the scope of this article, but keep in mind that within the current release Content Types are created and maintained within a Site Collection.  If a Content Type applies to multiple site collections then it needs to be duplicated.  SharePoint 2010 will support farm level content types which will remove the need to duplicate them.

Profiles (WSS Only) – If you are using WSS it is important to understand that the profiles are stored at the Site Collection level.  If you add custom attributes they will need to be added to all site collections they apply to.

Implications on Backup and Recovery

There are a few different methods to backup and recover SharePoint content.  Within the context of this article it is important to understand the difference between the commands that stsadm provides. 

Site Collection Backup and Restore – Considering the boundaries previously discussed, there is a lot of extra content stored in the top level site of a site collection.  Doing an stsadm backup will provide a high fidelity snapshot of the content, configuration, workflows, and other customizations.  It is important to note that if you are moving the site collection between applications or farms that you will need to install any solutions or dependencies referenced in the current location.

Sub-Site or Web Export and Import – The Export process offered for Sub-Sites is great for archiving, but it does not provide the fidelity needed to move sites around.  It will not save workflow, features, solutions or alerts.  I’ve also had inconsistent results with DataViews on sites being migrated in this manner.

If you think you will need to migrate the content or want flexibility, it would be in your best interest to consider using more Site Collections rather than deeply nested Sub-Sites.

Upgrade and Migration Considerations

The purpose and content within a site collection or site can evolve over time.  Some sites that started very narrow in purpose may change and now warrant their own Site Collection or Application.  When preparing for a migration or upgrade it is a good time to run through this exercise again to validate the assumptions and decisions that were previously made or overlooked.  While it may make the move more difficult the changes will pay dividends over the coming years offering a system better tuned to the user’s needs and more maintainable by the site owners and farm administrators.

Related Posts

%d bloggers like this: