Article Creating Activity Stories

Activity stories are shown in the activity stream and provide an easy way for users to stay engaged with a community.

Why should I create Activity Stories?

When performing actions upon a community providing a stream of events will allow the users to view relevant activity at a glance. A good example is when a user adds an event to the calendar an activity story can provide users with information about the event without requiring navigating to the calendar. Verint Community provides support for posting stories in the activity stream via the IActivityStoryType Plugin Type.

Creating an Activity Story Type

To add support for creating stories, implement the IActivityStoryType plugin (which requires a reference to Telligent.Evolution.Api.dll, Telligent.Evolution.Components.dll and Telligent.Evolution.Core.dll). The IActivityStoryType interface extends IPlugin to add support for activity stories.

To begin you'll need to generate a GUID and return it as the StoryTypeId

public Guid StoryTypeId
{
    get { return new Guid("247A9821-75F0-4E57-81E2-15B9D0578491"); }
}

After setting up the Id the story type requires a StoryTypeName and StoryTypeDescription. This name and description is used to identify the story type in administration and stream configuration user interfaces.

public string StoryTypeName
{
    get { return "My Activity Stories"; }
}

public string StoryTypeDescription
{
    get { return "Controls activity stories for My Activity Stories."; }
}

Activity stories can allow the user to delete an individual story. The CanDeleteStory method accepts a storyId and a userId. These id's can be used to find a particular story as well as a user. Then depending on your requirements return a boolean value that will identify if the user can remove the provided story. In this sample we verify the user is logged in and not an anonymous user then we determine if the user is one of the actors of the story. An actor can be defined as a user or users that took part in a particular activity.

public bool CanDeleteStory(Guid storyId, int userId)
{
    Apis.Get<EventLog>().Write("", new EventLogEntryWriteOptions());
    // unregistered users cannot delete stories
    var user = Apis.Get<Users>().Get(new UsersGetOptions { Id = userId });
    var userIsRegistered = user != null && !user.IsSystemAccount.GetValueOrDefault(false);
    if (!userIsRegistered)
    {
        return false;
    }

    // actors can delete
    var story = Apis.Get<ActivityStories>().Get(storyId);
    return story != null && story.Actors != null && story.Actors.ToList().Exists(actor => actor.UserId == userId);
}

The ContentTypeIds property returns a list of content type IDs related to the content that the stories belong to (see |creating content for more details about content types). Typically it is related to one content type but can be set to multiple types.

public Guid[] ContentTypeIds
{
    get { return new[] { Apis.Get<Users>().ContentTypeId }; }
}

In some cases the story must present a summary of the activity. Usually it is recommended to limit the summary to a single sentence. This is done in the GetPreviewHtml method.

public string GetPreviewHtml(IActivityStory story, Target target)
{
    var content = Apis.Get<Contents>().Get(story.ContentId.GetValueOrDefault(), story.ContentTypeId.GetValueOrDefault());
    if (content == null) return null;

    return string.Format("<h3>{0}</h3>", content.HtmlName("Web"));
}

The full view of the story will be processed through the GetViewHtml method. This method receives two parameters IActivityStory story and Target target. The IActivityStory parameter can be used to retrieve the content associated with the story. This is used to build the HTML response and the target identifies where the rendered content will be included (such as Web, Email or Other) to enable the story to render an appropriate version of the story.

public string GetViewHtml(IActivityStory story, Target target)
{
    var content = Apis.Get<Contents>().Get(story.ContentId.GetValueOrDefault(), story.ContentTypeId.GetValueOrDefault());
    if (content == null) return null;

    return string.Format("<h3>{0}</h3><p>User was authenticated.</p>", content.HtmlName("Web"));
}

The community also needs to know who the primary user is for a given story. This process is left up to the story type via the GetPrimaryUser method. In this sample we simply return the last actor that is responsible for the given story.

public int? GetPrimaryUser(IActivityStory story)
{
    if (story == null || story.Actors == null || story.Actors.Count == 0)
    {
        return null;
    }

    return story.Actors.Last().UserId;
}

The controller is the final member of the activity story type. The IActivityStoryController provides the functionality to create, update or delete stories in Verint Community.

private IActivityStoryController _storyController;

public void SetController(IActivityStoryController controller)
{
    _storyController = controller;
}

Here's the complete source of our sample activity story type plugin:

using System;
using System.Collections.Generic;
using System.Linq;
using Telligent.Evolution.Api.Services;
using Telligent.Evolution.Extensibility;
using Telligent.Evolution.Extensibility.Api.Version1;
using Telligent.Evolution.Extensibility.Content.Version1;
using ActivityStoryActor = Telligent.Evolution.Extensibility.Content.Version1.ActivityStoryActor;

namespace MyActivityStory
{
    public class MyActivityStory : IActivityStoryType
    {
        public string Name
        {
            get { return "My Activity Story Plugin"; }
        }

        public string Description
        {
            get { return "This plugin will demo how the IActivityStoryType works"; }
        }

        public void Initialize()
        {
            Apis.Get<Users>().Events.AfterAuthenticate += Events_AfterAuthenticate;
        }

        private void Events_AfterAuthenticate(UserAfterAuthenticateEventArgs e)
        {
            UserAuthenticatedStory(e.Id.GetValueOrDefault());
        }
        
        public Guid StoryTypeId
        {
            get { return new Guid("247A9821-75F0-4E57-81E2-15B9D0578491"); }
        }

        public string StoryTypeName
        {
            get { return "My Activity Stories"; }
        }

        public string StoryTypeDescription
        {
            get { return "Controls activity stories for My Activity Stories."; }
        }
        
        public bool CanDeleteStory(Guid storyId, int userId)
        {
            // unregistered users cannot delete stories
            var user = Apis.Get<Users>().Get(new UsersGetOptions { Id = userId });
            var userIsRegistered = user != null && !user.IsSystemAccount.GetValueOrDefault(false);
            if (!userIsRegistered)
            {
                return false;
            }

            // actors can delete
            var story = Apis.Get<ActivityStories>().Get(storyId);
            return story != null && story.Actors != null && story.Actors.ToList().Exists(actor => actor.UserId == userId);
        }

        public Guid[] ContentTypeIds
        {
            get { return new[] { Apis.Get<Users>().ContentTypeId }; }
        }

        public string GetPreviewHtml(IActivityStory story, Target target)
        {
            var content = Apis.Get<Contents>().Get(story.ContentId.GetValueOrDefault(), story.ContentTypeId.GetValueOrDefault());
            if (content == null) return null;

            return string.Format("<h3>{0}</h3>", content.HtmlName("Web"));
        }

        public string GetViewHtml(IActivityStory story, Target target)
        {
            var content = Apis.Get<Contents>().Get(story.ContentId.GetValueOrDefault(), story.ContentTypeId.GetValueOrDefault());
            if (content == null) return null;

            return string.Format("<h3>{0}</h3><p>User was authenticated.</p>", content.HtmlName("Web"));
        }

        public int? GetPrimaryUser(IActivityStory story)
        {
            if (story == null || story.Actors == null || story.Actors.Count == 0)
            {
                return null;
            }

            return story.Actors.Last().UserId;
        }

        private IActivityStoryController _storyController;

        public void SetController(IActivityStoryController controller)
        {
            _storyController = controller;
        }

        public bool IsCacheable
        {
            get { return true; }
        }

        public bool VaryCacheByUser
        {
            get { return true; }
        }

        private void UserAuthenticatedStory(int userId)
        {
            var user = Apis.Get<Users>().Get(new UsersGetOptions { Id = userId });
            
            _storyController.Create(new ActivityStoryCreateOptions
            {
                ContentId = user.ContentId,
                ContentTypeId = Apis.Get<Users>().ContentTypeId,
                LastUpdate = DateTime.UtcNow,
                Actors = new List<ActivityStoryActor>
                {
                    new ActivityStoryActor 
                    { 
                        UserId = user.Id.GetValueOrDefault(),
                        Verb = "Add",
                        Date = DateTime.UtcNow
                    }
                }
            });
        }

    }
}

Using the Activity Story Controller

In this sample we added a private method called UserAuthenticatedStory. It is called when a user is authenticated, it demonstrates how to create a new story in the activity stream. 

public void Initialize()
{
    Apis.Get<Users>().Events.AfterAuthenticate += Events_AfterAuthenticate;
}

private void Events_AfterAuthenticate(UserAfterAuthenticateEventArgs e)
{
    UserAuthenticatedStory(e.Id.GetValueOrDefault());
}

.
.
.

private void UserAuthenticatedStory(int userId)
{
    var user = Apis.Get<Users>().Get(new UsersGetOptions { Id = userId });
    
    _storyController.Create(new ActivityStoryCreateOptions
    {
        ContentId = user.ContentId,
        ContentTypeId = Apis.Get<Users>().ContentTypeId,
        LastUpdate = DateTime.UtcNow,
        Actors = new List<ActivityStoryActor>
        {
            new ActivityStoryActor 
            { 
                UserId = user.Id.GetValueOrDefault(),
                Verb = "Add",
                Date = DateTime.UtcNow
            }
        }
    });
}

Here is a sample of what the story looks like in the activity stream.