Showing posts with label Newsfeed. Show all posts
Showing posts with label Newsfeed. Show all posts

Sunday, 20 January 2013

Creating Custom Activity Type - Part 3


​Hello Readers,

Welcome back to the Part 3 of this series on creating custom activity type. Just to recap in Part 1 you read about how to get the custom activity type available in your settings page in addition to OOB SharePoint  types. In Part 2 you read about how to get feed related to your custom feed type in your colleagues feed as well as in your feed. So functionality wise we have been able to cover everything. But there is one thing still pending and that is what is happening behind the scenes. When I was trying to implement this feature, I was very anxious in knowing where is the data actually going? So, i explored the content db and found out that it modifies the PROFILE DB and SOCIAL DB. The various tables of interest here are


  1. ActivityPreference : This is the table that acts as a filter. If you remember, when you go to your  newsfeed settings page and try to track down all the activities that you are following then by default you will observe that everything is coming selected. If for any user , all the options are selected for following then there will be NO entry created in the ActivityPreference table. It will create an entry only if any user selects some limited options i.e. why a filter.
  2. ActivityTemplate :  As the name suggests, it is the template for our activity type. We define the Activity Template inside a resource file.The ActivityTemplate table contains the list for all the activity types. Below is the screenshot of this table where if you observe, in the ActivityTemplateId we have the unique ID for our CustomActivityType. If you recall , in Part 1, we had created an ActivityTemplate and ActivityType. Try debugging the code and check the value of these id's and then open up this table in your Profile DB and you will see a record gets created here for your custom activity template.

 
    3. ActivityApplication : If you recall, to start with creating a custom activity type, we had first of all created a custom Activity Application. This information gets stored in the ActivityApplication table in the ProfileDB.


 4.  ActivityType : This table contains the list of all the activity types created. In the table below, if you observe we have our custom Activity Type. If you observe carefully, there is a relation between all these tables that maps one thing to the other like the ActivityTypeId , ApplicationID, ApplicationTemplateId etc.


 5. ActivityEventsConsolidated : This table contains all the events that will appear in the colleagues  newfeed page.


 6. ActivityEventsPublished : This table contains all the events that will appear in the publisher's Recent activities webpart.


So, here I come to the end of the series of the blog posts on custom activity type. I hope that by now you must have got some idea on how to achieve this functionality.


Happy SharePointing !!!!!!

- Geetanjali





Saturday, 19 January 2013

Creating Custom Activity Type - Part 2


​In the previous blog on creating custom activity type Part 1​ , I have shown how to add a custom activity type to appear in the Newsfeed Settings page. In this part I will show how to create activity events so that it starts  to appear in your activity feed. The most important object that you require in order to create an Activity Event is the ActivityManager. But it brings with it some problems. If you look at MSDN , ActivityManager has a constructor


public ActivityManager(
UserProfile userProfile,
SPServiceContext serviceContext
)


Parameters
userProfile : This value must be identical to the UserProfile object that represents the current user.
serviceContext : Default context for this ActivityManager

Here is the problem. The constructor says that the userProfile MUST represent the current user. Now, what will happen if the logged in user is not the administrator and is a normal user with limited permissions?
The user will get an error message stating that the user does not have the required permissions. As a SharePoint developer, we can think of elevating the SPSite object and passing the System Account user to create the ActivityManager object. But it is not simple as it seems to be. Even though you pass the elevated user, still it will pick user from the current context. So how to go about solving this problem. The solution that works over here is to modify the HTTPContext. It seems that the User Profile Service Application depends
upon the HTTPContext . So the workaround to solve this issue is that we need to store the current HTTPContext  in some temporary variable so that later you can set the context back to  the original, impersonate the context, perform your operations and finally set the context back to the original one. Below is the code snippet as to how you can do the same.


HttpContext httpContext = HttpContext.Current;
SPUser user = SPContext.Current.Web.CurrentUser;
string userName = user.LoginName;
try
{
    SPServiceContext currentContext = SPServiceContext.Current;
    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, adminUser);
    HttpContext.Current.User = new GenericPrincipal(wi, new string[0]);
             
    serviceContext = SPServiceContext.GetContext(HttpContext.Current);
    UserProfileManager pm = new UserProfileManager(serviceContext);
    UserProfile current = null;
             
    if (pm != null)
    {
        SPUser adminUser = web.EnsureUser(adminUser);
        if (user.IsSiteAdmin)
            current = upm.GetUserProfile(adminUser.LoginName);
        HttpContext.Current.Response.Write(current);

    }
    if (current != null)
        actMgr = new Microsoft.Office.Server.ActivityFeed.ActivityManager(current, serviceContext);
             
}
catch (Exception ex)
{
}
finally
{    
    //undo the impersonated httpcontext very much required
    HttpContext.Current = httpContext;
    WindowsIdentity wi = WindowsIdentity.GetCurrent();
    typeof(WindowsIdentity).GetField("m_name", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(wi, userName);
    HttpContext.Current.User = new GenericPrincipal(wi, new string[0]);
}

So, if you observe the above code snippet, First store the HTTPContext in a temporary variable httpContext. Second, generate a new context with impersonated user who has the permissions on the User Profile Service Application. Generate the ActivityManager instance using the elevated context. If you  observe now, you will not get any Permission related issue now. Please note that in the finally clause, we are setting the context back to the original one. It is IMPORTANT to set it back to original for obvious reasons. Having successfully generating the ActivityManager instance, you are very near to achieveing the actual motive and i.e. to have a custom feed in your Newsfeed settings page. Next step is to Create an event that will appear in your feed. Below is the sample code snippet to achieve this.


if (actMgr != null)
{
    long aId = actMgr.ActivityApplications["ActivityThemeApplication"].ActivityTypes["ActivityThemeType"].ActivityTypeId;
    if (aId != 0)
    {
        foreach (Colleague colleague in loggedInUser.Colleagues.GetItems())
        {
            CreateEvent(cssFilename, aId, colleague.Profile, actMgr, loggedInUser, themePageURL);
        }
    }
}
public void CreateEvent(string theme, long aId, UserProfile colleagueProfile, Microsoft.Office.Server.ActivityFeed.ActivityManager actMgr, UserProfile loggedInUser,string themePageURL)
{
    try
    {
             
      if (colleagueProfile != null)
      {
          Entity publisher = new MinimalPerson(loggedInUser).CreateEntity(actMgr);
          Entity owner = new MinimalPerson(colleagueProfile).CreateEntity(actMgr);
          ActivityEvent activityEvent = ActivityEvent.CreateActivityEvent(actMgr, aId, owner, publisher);
          activityEvent.Name = "ActivityThemeType";
          activityEvent.ItemPrivacy = (int)Privacy.Public;
          activityEvent.Owner = owner;
          activityEvent.Publisher = publisher;
          Link link = new Link();
          link.Name = "Click here to change your theme." ;
          link.Href = SPContext.Current.Site.Url + themePageURL;
          activityEvent.Link = link;
          activityEvent.Value = theme;
          activityEvent.Commit();
     
       }
     }
     catch (Exception ex)
     {
     }
}



loggedInUser : User Profile of the current logged in user
cssFileName  : It can be anything. In this case, this is the name of the theme that I want to set.
themePageURL : In this case this is the link for my custom page where users can change their themes. It appears in the newsfeed where colleagues can click to change their theme.


In the above code snippet, we are pushing this custom feed in the newsfeed page of all the colleagues of the current logged in user. Publisher is the current logged in user who is changing his theme and Owner is the colleague in whose newsfeed we want the notification to appear. Rest of the code is self-explanatory. The end result is that whenever a person changes his theme then all his colleagues will be notified of this change. In-case you want this to appear in the Recent Activities of the current logged in user who is changing the
theme, then there is a slight modification. In that case you simply need to sent the Owner and Publisher as the same and that is the current logged in user and that is it.



So we can see our custom type in the newsfeed. :)

In the next part I will explain what happens at the backend behind the scenes. Till then keep exploring. :)

- Geetanjali