A user's followed sites are stored within their personal site collection (i.e. their my site). By default, only the users themselves have access to their personal site collections (they are the primary site collection administrator). This is why the call to SPSocialFollowingManager.GetFollowed()
isn't returning any SPSocialActor
objects--the current user simply doesn't have access to the other user's personal site collection.
Depending on how your web applications are configured, elevating permissions may not be sufficient to resolve another user's followed sites because the application pool account under which the elevated code is running may not have access to the web application hosting the personal site collection of the user. Ultimately, SharePoint is doing the right thing here: security trimming the results of a list query based on the permissions of the current user.
If you grant the application pool account that your code is running under Read access to the My Site Host web application then you should be able to create an SPSite
object representing the "non-current" user's personal site collection. With this SPSite
object you can then new up an SPServiceContext
and eventually call SPSocialFollowingManager.GetFollowed()
in order to return the followed objects for that user. See this TechNet article for information on how to add a user policy to a web application (SharePoint 2013).
Okay, some code. In the snippet below, userName
is the LoginName
(i.e. account name) of the user whose followed sites we mean to get. This code is called from within a web part within a web application that is not configured as the My Site Host web application. However, the application pool account under which this code runs has been granted Read access to the My Site Host web application via Central Administration.
...
// get followed sites for a user that is not the current user
SPSecurity.RunWithElevatedPrivileges(() =>
{
// resolve the user's profile
UserProfile userProfile = null;
using (var elevatedSite = new SPSite(SPContext.Current.Site.ID))
{
var serviceContext = SPServiceContext.GetContext(elevatedSite);
var userProfileManager = new UserProfileManager(serviceContext);
if (userProfileManager.UserExists(userName))
{
userProfile = userProfileManager.GetUserProfile(userName);
}
}
if (userProfile != null)
{
// NOTE: for this to work properly the application pool account under which this code runs must have read permissions on the my site host web application
// new up the user's personal site collection and get the user's followed sites
using (var personalSite = new SPSite(userProfile.PersonalSite.ID))
{
var personalServiceContext = SPServiceContext.GetContext(personalSite);
// the next two lines are possibly redundant; these objects are recreated to ensure sanctity of context
var personalUserProfileManager = new UserProfileManager(personalServiceContext);
var personalUserProfile = personalUserProfileManager.GetUserProfile(userProfile.AccountName);
var personalFollowingManager = new SPSocialFollowingManager(personalUserProfile, personalServiceContext);
// if the user is following any sites, this array should not be empty
var followedSites = personalFollowingManager.GetFollowed(SPSocialActorTypes.Sites);
}
}
});
...
No hacky nulling out or faking of HttpContext.Current
necessary...
UserProfile.FollowedContent
, but you cannot easily go from an array ofFollowedItem
objects to an array ofSPSocialActor
objects, as far as I know.