Home | Blog | Screencasts | Projects
# Monday, November 23, 2009

One of the more interesting features of SharePoint 2010 is the activity feed, it’s kind of like the news feed in Facebook a whole range of system wide events are collated in a users my site, these can include events generated by colleagues of a user. I really think this is cool and we will see lots of applications make use of this feature. Even in a business environment the concept of a status feed is useful, SharePoint 2010 provides what seems like a nice enterprise wide service for collecting these events.

 

I thought it might be fun to have a play with some of the activity feed API’s to create something like a Twitter web part for SharePoint 2010, just to be upfront, this code isn’t really useable in a real sense, its just me playing around.

First I’ve created a visual web part for the user to enter a tweet:

 

image

 

Now if your a colleague, you can visit your ‘MySite’ and view the aggregated activities of your colleagues:

 

image

 

Here you can see that the user ‘administrator’ (this person’s colleague) has ‘tweeted’ something.

 

The SharePoint team have made this whole status/activity feed quite extensible, the first step is to define your own activity application

Setup:

	private ActivityApplication actApplication = null;
	private ActivityManager actMgr = null;
	private UserProfile currentUserProfile = null;
	private ActivityType tweetActivityType = null;
	private ActivityTemplate tweetActTemplate = null;

	private void SetupActivities()
         {
            Microsoft.Office.Server.UserProfiles.UserProfileManager pm = new UserProfileManager(SPServiceContext.Current);
            currentUserProfile = pm.GetUserProfile(Page.User.Identity.Name);
            actMgr = new ActivityManager(currentUserProfile);
            tweetActivityType = null;

            if (actMgr.PrepareToAllowSchemaChanges())
            {
                if (actMgr.ActivityApplications["SP2010Twitter"] == null)
                {

                    actApplication = actMgr.ActivityApplications.Create("SP2010Twitter");
                    actApplication.Commit();
                }
                else
                {
                    actApplication = actMgr.ActivityApplications["SP2010Twitter"];
                }

                tweetActivityType = actApplication.ActivityTypes["SP2010Tweeting"];
                if (tweetActivityType == null)
                {
                    tweetActivityType = actApplication.ActivityTypes.Create("SP2010Tweeting");
                    tweetActivityType.ActivityTypeNameLocStringName = "ActivityName";
                    tweetActivityType.ActivityTypeNameLocStringResourceFile = "SP2010Twitter";
                    tweetActivityType.IsPublished = true;
                    tweetActivityType.IsConsolidated = true;
                    tweetActivityType.AllowRollup = true;
                    tweetActivityType.Commit();
                }

                tweetActTemplate = tweetActivityType.ActivityTemplates[ActivityTemplatesCollection.CreateKey(false)];
                if (tweetActTemplate == null)
                {
                    tweetActTemplate = tweetActivityType.ActivityTemplates.Create(false);
                    tweetActTemplate.TitleFormatLocStringResourceFile = "SP2010Twitter";
                    tweetActTemplate.TitleFormatLocStringName = "Activity_Created";
                    tweetActTemplate.Commit();
                }
            }
            else
            {
                //not a profile admin

                var activities = actMgr.GetActivitiesForMe();
                foreach (var act in activities)
                {
                    if (act.Name == "SP2010Twitter")
                    {
                        actID = act.ActivityTypeId;
                    }
                }
            }
            
            

        }

The code above needs to be run once, to setup the application and to define the templates etc.

Creating an event:

	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);
            activityEvent.Name = "SP2010Twitter";
            activityEvent.ItemPrivacy = (int)Privacy.Public;
            activityEvent.Owner = owner;
            activityEvent.Publisher = publisher;            
            
            activityEvent.Value = tweet;
            activityEvent.Commit();

            
            return activityEvent;
        }

Publishing the event to the user's colleagues:

 

	public void MulticastPublishedEvents()
	{
            if (activityEvents.Count == 0)
                return;

            List publishers = new List();
            foreach (ActivityEvent activityEvent in activityEvents)
            {
                if (!publishers.Contains(activityEvent.Owner.Id))
                    publishers.Add(activityEvent.Owner.Id);
            }

            Dictionary owners;
            Dictionary> colleaguesOfOwners;
            ActivityFeedGatherer.GetUsersColleaguesAndRights(ActManager, publishers, out owners, out colleaguesOfOwners);

            Dictionary> eventsPerOwner;
            ActivityFeedGatherer.MulticastActivityEvents(ActManager, activityEvents, colleaguesOfOwners, out eventsPerOwner);

            List eventsToMulticast;
            ActivityFeedGatherer.CollectActivityEventsToConsolidate(eventsPerOwner, out eventsToMulticast);
            WriteEvents(eventsToMulticast);
	}

	private void WriteEvents(List events)
	{
            int startIndex = 0;
            while (startIndex + ActManager.MaxEventsPerBatch < events.Count)
            {
                ActivityFeedGatherer.BatchWriteActivityEvents(events, startIndex, ActManager.MaxEventsPerBatch);
                startIndex += ActManager.MaxEventsPerBatch;
            }
            ActivityFeedGatherer.BatchWriteActivityEvents(events, startIndex, events.Count - startIndex);
        }
            

Publishing an event is a little more involved and this code was drawn heavily from the SDK sample found here.

The main classes that get used here are:

 

ActivityApplication

An activity application is simply the name of the application, the ‘NewsFeed Settings’ on the my site gives the end user the ability to participate with your application:

 

 image

 

ActivityManager

Simple manager class that gets initialised with the current user profile, this class gets passed around to the other classes that create events.

 

ActivityType

This class provides us with the ability to actually define the activity, things like the time to live, is rolled up? and the template to use. This class also requires the developer to define the resources file that will be used to display the activity name.

 

ActivityTemplate

The display of the activity event gets managed by this class, you need to specify a resources file and key that contains the elements to create the display text. This class and associated resources file controls the UI presented to the user.

 

ActivityEvent

This is the actual event, here we define the publisher and other things like the item privacy level, it’s possible to create public and private events.

 

ActivityFeedGatherer

The gatherer is probably the least intuitive class, it provides the ability to batch write the events, this is handy because often we will want to publish the events to all of the colleagues of the current user.

 

Resources File:

Like I mentioned above, all of the templates and UI elements must be in a resx file. This file must be deployed to:

 

{SharePointRoot}\Resources\SP2010Twitter.resx

 

And will contain something like (note how the ActivityName and Activity_Created are used in the code above):

 

<data name="ActivityName" xml:space="preserve">
    <value>SP2010Twitter</value>
</data>
<data name="Activity_Created" xml:space="preserve">
    <value>{Publisher} tweeted: {Value}</value>
</data>

 

Some of the Default Template Variables:

{Link}

{Value}

{Publisher}

{Size}

 

So above I made use of the {Value} variable and populated the value property of the event object with the tweet value. I could change the resx definition which would change the way the event is shown to the user.

 

Problems:

 

I’ve come across a few little problems, firstly it seems like the user creating the event needs to be a profile admin, even a call to ActivityEvent.CreateActivityEvent seems to require the profile admin property. Unless I’m missing something, I hope this changes in the RTM environment.

 

You can download the sample code here

Sunday, November 22, 2009 11:49:33 PM (E. Australia Standard Time, UTC+10:00)
Great post mate! good to see people jumping into the API and pushing the extensibility straight away!
Comments are closed.
Statistics
Total Posts: 191
This Year: 0
This Month: 0
This Week: 0
Comments: 41