Friday 27 August 2010

Create Activity Feed Event by Non Admin Users

To Start with i was really excited about Activity Feed Stuff Sharepoint 2010 is providing but then when i tried using object model i realised SPServiceContext doesnt support impersonation directly. It requires HttpContext to be impersonated so that a non User Profile Admin User can also create Events .After lots of googling and modifying/updating the logic a bit , just want to share how i did this

SPServiceContext poweredCtx = null;
SPUser currentUser = SPContext.Current.Web.CurrentUser;
HttpContext currentHttpContext = HttpContext.Current;
try
{
Guid siteId = SPContext.Current.Site.ID;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite aSite = new SPSite(sampleSite))
{
using (SPWeb web = aSite.OpenWeb())
{
SPServiceContext currentCtx = SPServiceContext.Current;
SPUser user = web.CurrentUser;
web.AllowUnsafeUpdates = true;
/*
* Impersonate here ....
*/
HttpRequest request = new HttpRequest("", web.Url, "");
HttpContext.Current = new HttpContext(request, new HttpResponse(new StringWriter(CultureInfo.CurrentCulture)));
HttpContext.Current.Items["HttpHandlerSPWeb"] = web;
WindowsIdentity wi = WindowsIdentity.GetCurrent();
typeof(WindowsIdentity).GetField("m_name", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(wi, user.LoginName);
HttpContext.Current.User = new GenericPrincipal(wi, new string[0]);
WindowsIdentity wi2 = WindowsIdentity.GetCurrent();
poweredCtx = SPServiceContext.GetContext(HttpContext.Current);

m_UPM = new UserProfileManager(poweredCtx);
/*
* Create Events here and publish them
*/
}
}
});
}
finally
{
//undo the impersonated httpcontext very much required
HttpContext.Current = currentHttpContext;
WindowsIdentity wi = WindowsIdentity.GetCurrent();
typeof(WindowsIdentity).GetField("m_name", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(wi, currentUser.LoginName);
HttpContext.Current.User = new GenericPrincipal(wi, new string[0]);
}


Big Thanks to STEVE CURRAN
Reference :http://sharepointfieldnotes.blogspot.com/2010/02/creating-sp2010-social-comments.html

6 comments:

  1. Hi,
    Thanks for the post.
    I tried to create an activtyfeed same as one explained here:
    http://httpcode.com/blogs/CommentView,guid,b3af80e2-4e6f-41d6-ae93-61950a332a39.aspx
    The following line in the above blog is giving me error saying that "you must be an user profile admin to perform this operation",

    public ActivityEvent GenerateActivityEvent(string tweet, long activityId)
    {
    Entity owner = new MinimalPerson(CurrentUserProfile).CreateEntity(ActManager);
    Entity publisher = new MinimalPerson(CurrentUserProfile).CreateEntity(ActManager);
    ActivityEvent activityEvent = ActivityEvent.CreateActivityEvent(ActManager, activityId, owner, publisher);

    I finally found your blog and tried it within the same method as fallows:
    SPServiceContext poweredCtx = null;
    SPUser currentUser = SPContext.Current.Web.CurrentUser;
    HttpContext currentHttpContext = HttpContext.Current;
    try
    {
    Guid siteId = SPContext.Current.Site.ID;
    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
    using (SPSite aSite = new SPSite(siteId))
    {
    using (SPWeb ElevatedSite = aSite.OpenWeb())
    {
    SPServiceContext currentCtx = SPServiceContext.Current;
    SPUser user = ElevatedSite.CurrentUser;
    ElevatedSite.AllowUnsafeUpdates = true;
    HttpRequest request = new HttpRequest("", ElevatedSite.Url, "");
    HttpContext.Current = new HttpContext(request, new HttpResponse(new StringWriter(CultureInfo.CurrentCulture)));
    HttpContext.Current.Items["HttpHandlerSPWeb"] = ElevatedSite;
    WindowsIdentity wi = WindowsIdentity.GetCurrent();
    typeof(WindowsIdentity).GetField("m_name", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(wi, user.LoginName);
    HttpContext.Current.User = new GenericPrincipal(wi, new string[0]);
    WindowsIdentity wi2 = WindowsIdentity.GetCurrent();
    poweredCtx = SPServiceContext.GetContext(HttpContext.Current);

    UserProfileManager pm = new UserProfileManager(poweredCtx);
    CurrentUserProfile = pm.GetUserProfile(Page.User.Identity.Name);
    ActManager = new ActivityManager(CurrentUserProfile);

    Entity owner = new MinimalPerson(CurrentUserProfile).CreateEntity(ActManager);
    Entity publisher = new MinimalPerson(CurrentUserProfile).CreateEntity(ActManager);

    activityEvent = ActivityEvent.CreateActivityEvent(ActManager, activityId, owner, publisher);
    activityEvent.Name = activityType.ActivityTypeName;
    activityEvent.ItemPrivacy = (int)Privacy.Public;
    activityEvent.Owner = owner;
    activityEvent.Publisher = publisher;

    activityEvent.Value = tweet;
    currentWeb.AllowUnsafeUpdates = true;
    activityEvent.Commit();
    currentWeb.AllowUnsafeUpdates = false;
    }
    }
    });
    }
    finally
    {
    //undo the impersonated httpcontext very much required
    HttpContext.Current = currentHttpContext;
    WindowsIdentity wi = WindowsIdentity.GetCurrent();
    typeof(WindowsIdentity).GetField("m_name", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(wi, currentUser.LoginName);
    HttpContext.Current.User = new GenericPrincipal(wi, new string[0]);
    }

    But I am still getting the same error. Any help is really appreciated.

    Thanks,
    Anu.

    ReplyDelete
  2. can u tell me which line is throwing the error ? is it activityEvent.Commit() ? And i hope App Pool Identity has rights to create activity ?

    ReplyDelete
  3. Hi I'm trying to use the above code to update User Profile without the user performing the operation is added as an administrator in the User Profile Service. In short the application I'm trying to built is to let HR staff update their user profiles.

    Can you please help me. I've tried your code and Steve code and still get the error Attempted to perform unauthorized operation when I hit this line
    UserProfile["propertyname"].Value = newValue;

    ReplyDelete
  4. Try This:

    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
    HttpContext httpCxt = HttpContext.Current;
    var context = ServerContext.GetContext(httpCxt);
    HttpContext.Current = null; // Hack !!!

    //Create your event
    CreateEvent(_tbActivity.Text, aId, actMgr, publisher, publisher);

    HttpContext.Current = httpCxt; // Restore !!!
    });

    ReplyDelete
  5. The prevoius code works!!
    After i created an ActivityEvent (activityEvent.Commit()) the ActivityEventId =-1

    I tried to create a SyndicationItem:
    SyndicationItem syndi = activityEvent.CreateSyndicationItem(actMgr.ActivityTypes, ContentType.Html);

    But syndi.Id = -1 activityEvent.ActivityEventId =-1 :(

    Executing ActivityManager.GetActivitiesByMe();
    I find the event with the ActivityEventId set

    Is there a way to get the ActivityEventId after I'm creating the event?

    Pelase help me

    ReplyDelete
  6. Thank you so much :)

    ReplyDelete