NOTE:  Please download the attachment to get a Word doc with this posting in human readable format.

I had an interesting request recently from someone that wanted help essentially being able to migrate social tags between two different farms.  There are some things out of the box that can help you do this, and some things that are tantalizing, but not quite sufficient.

I tested getting the social tags themselves without any kind of “real” impersonation.  It worked for me but I can’t guarantee that it will in all cases because I was logged in as UPA admin.  If it does not, then you could use the same impersonation technique I’ll describe down below.  To get the tags for a user, I just tried to create a SPSite context as that user, then create all the context objects needed to retrieve tags for that user, like this:

SPUserToken sut = null;

//get the user token for user first so we can

//use that to get a site context as that user

using (SPSite userSite = new SPSite(UrlTxt.Text))

{

   using (SPWeb rootWeb = userSite.RootWeb)

   {

       SPUser socialUser =

          rootWeb.EnsureUser(AccountTxt.Text);

       sut = socialUser.UserToken;

   }

}

UPDATE:  Make sure you read this post to see how you should format the user name for EnsureUser:  http://blogs.technet.com/b/speschka/archive/2013/02/03/what-you-need-to-know-about-using-ensureuser-in-claims-based-web-apps-in-sharepoint-2010-and-sharepoint-2013.aspx.                

//now get the site as that user – NOTE: these are

//all class scoped variables

using (SPSite newSite = new SPSite(UrlTxt.Text, sut))

{

   sc = SPServiceContext.GetContext(newSite);

   stm = new SocialTagManager(sc);

   upm = new UserProfileManager(sc);

   up = upm.GetUserProfile(AccountTxt.Text);

}

 

Once I have the contexts that were created as the current user, then it’s pretty easy to get the tags for that user:

SocialTag[] tags = stm.GetTags(up);

 

TagLst.Items.Clear();

foreach (SocialTag tag in tags)

{

   TagLst.Items.Add(tag.Term.Name + " - " + tag.Url.ToString());

}

 

This part was fairly straightforward and easy.  Writing a social tag for a different user was unfortunately not nearly as easy.  The SocialTagManager includes an AddTag method, but it doesn’t provide an overload that includes a UserProfile like the GetTags method does.  This is a tough oversight, and unfortunately using the context of the user that was passed into the new SPSite constructor does not help.  As a result, you need to use impersonation to do this.  In this case I just reused the approach I described in this posting – http://blogs.technet.com/b/speschka/archive/2011/08/07/using-saml-claims-sharepoint-wcf-claims-to-windows-token-service-and-constrained-delegation-to-access-sql-server.aspx.  I configured the CTWTS to allow me (since the my application was running in my user context) to impersonate users.  Specific details on how to do that are described here:  http://msdn.microsoft.com/en-us/library/ee517258.aspx.

So with that approach in mind, here’s how I did the impersonation first:

//start the impersonation

//create the WindowsIdentity for impersonation

WindowsIdentity wid = null;

 

try

{

   wid = S4UClient.UpnLogon(EmailTxt.Text);

}

catch (SecurityAccessDeniedException adEx)

{

   MessageBox.Show("Could not map the Email to " +

       "a valid windows identity: " + adEx.Message);

}

 

//see if we were able to successfully login

if (wid != null)

{

   using (WindowsImpersonationContext ctx = wid.Impersonate())

   {

       //code goes here to add a new tag

   }

}

else

{

   MessageBox.Show("Couldn't impersonate user - can't add tag.");

}

 

The impersonation code itself is not particularly complex – you just need the user’s Email address (which you could probably get from their UserProfile, which this code retrieves).  You also need the CTWTS running, which installs on each SharePoint server, and this code needs to run on a SharePoint server since it uses the object model.  So again, this should not be a significant hurdle.

Finally, adding the new tag for the user does require jumping through a few hoops but is pretty manageable.  Here’s what that code looks like:

//this is the code that gets the SPSite, SPServiceContext, etc

GetServiceContext();

 

//work with the taxonomy classes so we

//can reuse any existing term, or create a

//new one if it doesn't exist

TaxonomySession txs = stm.TaxonomySession;

TermStore ts = txs.DefaultKeywordsTermStore;

 

TermCollection terms =

   ts.KeywordsTermSet.GetTerms(TagTxt.Text,

   ts.DefaultLanguage, true);

 

Term t = null;

 

if (terms.Count == 0)

{

   t = ts.KeywordsTermSet.CreateTerm(TagTxt.Text,

       ts.DefaultLanguage);

       ts.CommitAll();

}

else

   t = terms[0];

 

//add the tag

stm.AddTag(new Uri(TagUrlTxt.Text), t);

 

So what happens here is we just look in the taxonomy store for the tag that is being added.  If we find it in there then we use that term, if not we create it and add it to the term store.  Then we add the term and associate it with a Url and it gets added to that user’s social tags.

Overall the code and approach is fairly straightforward.  The main problem here is just working around the inability to specify to which user a social tag should be added.  The impersonation code and CTWTS takes care of that part for us without requiring the password for each user.  The source code for this project is attached to the posting.