You may also be interested in: SharePoint Solutions In-A-Box from Alcero
Editor’s note: Contributor Chris Grist is a SharePoint Architect at Beach Energy. Follow him @gristdog
For those of you who have checked out the Office 365 or SharePoint 2013 preview, you would have noticed that Microsoft has added a follow button, on sites and documents, the functionality when clicked basically bookmarks the site similar to a favourite in your web browser.
I have been working on a SharePoint 2010 project for our company, in the new web application there will be 100s of site collections and inside each 100s of sites. The sites are organised logically in a hierarchy, the challenge was that some users will always work in level 4 of the hierarchy and some will work at level 1 or somewhere in-between. As this requirement is not really prescriptive and we cannot know which user wants to navigate where, I needed to develop follow functionality similar to what Microsoft is shipping in SharePoint 2013.
This article looks at the setup and the code that has made this possible and a very popular feature with the end users thus far.
The first step is to decide upon where to store the information, for this I decided to use the user’s profile, so if you’re running SP Foundation, stop here and think about where/how you may store the information instead.
To do this I simply added a new user property called “bimsFollow”. This property was simply a string (multi value) type.

I also made the property hidden from the end user.

The next step was to build a control to be able to follow or un-follow a particular site. For this functionality I decided upon using a user control, this makes it very simply to embed the functionality in an area of my custom master page. So the first step was to open a Visual Studio project and add a new user control, for this I called it “follow”.
The only thing I needed my user control to show was a hyperlink as to whether the user was following the current site or not, so at the bottom of the .ascx file, I added the following code:
|
<asp:HyperLink ID="link" NavigateUrl="" Text="" Target="_self" runat="server" /> |
The URL and Text will be dynamic based on whether the user is following the site or not, so here I have left it blank and run the control at the server so I have access to it in the code behind.
The next step was to open the code behind file (.ascx.cs). Inside the page_load function I started by adding a couple of Boolean values:
|
bool followMe = false; bool isFollowing = false; |
Before constructing the bulk of the functionality I wanted to test that everything was working, so I then added the code to set the hyperlink with the correct URL and text.
|
if (isFollowing) { link.NavigateUrl = SPContext.Current.Web.Url + "?followme=false"; link.Text = "Click to unfollow"; }else{ link.NavigateUrl = SPContext.Current.Web.Url + "?followme=true"; link.Text = "Click to follow";} |
I then registered the user control to the master page to make sure what I had setup was working so far, even though the if statement is always going to execute the else block at this stage. Sure enough it appeared to be working, with a bit of CSS styling I ended up with a user control that matched the style of my custom master page:

As you can see from the code above I decided to use query strings to toggle whether the user was following the site or not, so the first thing we had to do before the if statement is called is to get the value for the query string.
|
if (Request.QueryString["followme"] != "" && Request.QueryString["followme"] != null) { followMe = Convert.ToBoolean(Request.QueryString["followme"]); } |
If the value exists, then we set the followMe bool to be either true or false based on the input the user has provided on the hyperlink. The next block of code, simply provides the setup for the User Profile Service so that we are able to access the current users profile.
|
SPContext.Current.Web.AllowUnsafeUpdates = true; System.Security.PermissionSet ps = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted); ps.Assert(); Microsoft.SharePoint.SPServiceContext serviceContext = Microsoft.SharePoint.SPServiceContext.Current; UserProfileManager upm = new Microsoft.Office.Server.UserProfiles.UserProfileManager(serviceContext); ProfileSubtypePropertyManager pspm = upm.DefaultProfileSubtypeProperties; UserProfile profile = upm.GetUserProfile(SPContext.Current.Web.CurrentUser.LoginName); |
Once we have the profile object we are then able to retrieve the collection of value’s that are currently stored in the user property we created earlier.
|
UserProfileValueCollection collectionofPropertys = profile["bimsFollow"]; |
The property is actually returned as collection, rather than a delimited set of strings, so we can quite easily iterate over the collection, and then compare whether the stored data (i.e. set of URLs) match the URL of the current site, and therefore indicate the user is following the current site.
|
foreach (var st in collectionofPropertys) { if(st.Equals(SPContext.Current.Web.Url)) { isFollowing = true; } } |
We now have most of the functionality we need, the missing piece is simply to add or remove objects from the collectionofPropertys that we have retrieved above based on our previous bool. The first check we therefore do is to whether the followMe bool is true.
|
if (followMe) { isFollowing = true; collectionofPropertys.Add((Object)SPContext.Current.Web.Url); profile.Commit(); } |
If the value is true, then we toggle isFollowing to true, so our hyperlink is configured correctly, and then simply add the URL to our collection of strings in the user profile, finally committing any changes. The inverse is almost as simply, however to remove values you must know the index of that particular value in the collection, so we must first iterate over the collection, find the location of the matching URL and then remove it and commit as show below.
|
else if (followMe == false && Request.QueryString["followme"] != null) { isFollowing = false; int count = 0; foreach (var st in collectionofPropertys) { if (st.Equals(SPContext.Current.Web.Url)) { collectionofPropertys.RemoveAt(count); profile.Commit(); } count++; } } |
If all has gone successful you will have a fully functional hyperlink that toggles values when you select it:

If you then head over to the USPA and look at the user profile via central admin, you will see the URLs populated:

Now that this functionality is working, you simply need to display a listing of sites a user is following, all the code required is above, in my environment I created a web part, setup the connection to the USPA, grabbed the collection of property’s similar to this article and iterated over the values, opening the URLs and display the title of the site, the end result looked something like this:

The entire block of code for the functionality is below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
|
public partial class follow : UserControl { protected void Page_Load(object sender, EventArgs e) { bool followMe = false; bool isFollowing = false; if (Request.QueryString["followme"] != "" && Request.QueryString["followme"] != null) { followMe = Convert.ToBoolean(Request.QueryString["followme"]); } SPSecurity.RunWithElevatedPrivileges(delegate() { SPContext.Current.Web.AllowUnsafeUpdates = true; System.Security.PermissionSet ps = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted); ps.Assert(); Microsoft.SharePoint.SPServiceContext serviceContext = Microsoft.SharePoint.SPServiceContext.Current; UserProfileManager upm = new Microsoft.Office.Server.UserProfiles.UserProfileManager(serviceContext); ProfileSubtypePropertyManager pspm = upm.DefaultProfileSubtypeProperties; UserProfile profile = upm.GetUserProfile(SPContext.Current.Web.CurrentUser.LoginName); UserProfileValueCollection collectionofPropertys = profile["bimsFollow"]; foreach (var st in collectionofPropertys) { if(st.Equals(SPContext.Current.Web.Url)) { isFollowing = true; } } if (followMe) { isFollowing = true; collectionofPropertys.Add((Object)SPContext.Current.Web.Url); profile.Commit(); } else if (followMe == false && Request.QueryString["followme"] != null) { isFollowing = false; int count = 0; foreach (var st in collectionofPropertys) { if (st.Equals(SPContext.Current.Web.Url)) { collectionofPropertys.RemoveAt(count); profile.Commit(); } count++; } } }); if (isFollowing) { link.NavigateUrl = SPContext.Current.Web.Url + "?followme=false"; link.Text = "Click to unfollow"; } else { link.NavigateUrl = SPContext.Current.Web.Url + "?followme=true"; link.Text = "Click to follow"; } } } |