Creating feeds in Clubs and for external events
This post actually explains two ideas: Feeds for clubs and (feed) stories for objects external to the EPiServer Community platform.
Aggregating a feed for a Club
The EPiServer Community NewsFeed system is, in it’s vanilla form, very user-centric. Either you aggregate feeds for a particular user (the minifeed) or for a user’s friends (the newsfeed).
A quick recap of the NewsFeed system:
- The newsfeed aggregates Stories.
- You as a developer decide how Stories are created when something “happens” in your site, they aren’t auto-generated.
- A Story stores tuplets of the type actor – action – target, or: someone (“User A”) performed some action on (“commented on”) something (“user B’s blog post”).
- Stories can have multiple actors and multiple targets. The actors are IAuthors (generally a UserAuthor) and the Targets are Community entities.
- Stories can have Attachments (actually the are attached to the actor) that are other entities.
The key to (more easily) creating a news feed for something other than a user (the actor) is the NewsFeedHandler.GetNewsFeedStoriesByTarget method. As the method name indicates it aggregates stories by their target rather than the actor as is the case when building mini- and newsfeeds. This can be used to create a feed in a Club which for example shows the stories created when users join the Club, because those stories should generally include the Club in it’s targets. “User A joined this club”, could be an interesting piece of information, but what if you want more…
In Wonderland: Targets as “actors”
In a current project the Clubs are not created by site users or moderators which is common, but are created in code to reflect “departments”. So the Clubs could be seen as profiles for the departments just like the user profiles (MyPages).
Without going in to details, the Clubs can even act on their own, which should make them actors in Stories, right? But instead we use the target feed to get those stories. As actor we simply use the Club’s Author (which is a system user since the Clubs are created by the system but have to have an author) and the Club is still the target. But then we stuff whatever is the “real” target of the action in the StoryAttachments:
// Get some club to create a story forClub club = GetClub();// Get some entity to attach and some action used for this entityIEntity realTarget = GetRealTarget();string action = "MyAction";// Create attachments to the storyvar storyAttachments = new EntityCollection();storyAttachments.Add(realTarget);// Use the club's Author as actorvar author = AuthorHandler.Instance.ChangeAuthor(null, club.Author);// Create the story with attachmentvar story = new NewsFeedStory(new NewsFeedAction(action, NewsFeedActionCapability.MultipleTargets| NewsFeedActionCapability.MultipleActors),author, storyAttachments, club);// Persist the story to databaseNewsFeedHandler.Instance.AddStory(story);
Then we can pull out a feed for the Club using GetNewsFeedStoriesByTarget and render stuff like “Club A [target] posted [action] a new KPI [story attachment]”.
We can even mix these things in the users’ newsfeeds by getting the target feeds for Clubs in which they have memberships. That requires several calls and makes the paging features useless, but as the number of memberships is generally limited and the newsfeed only shows a few items without paging we accepted this performance penalty.
Creating stories for actions outside the Community
Since the departments also use other systems other actions that are of interest to the users can happen to “entities” outside the Community platform. In a deep integration scenario we would create custom Entity Providers for these external entities and either reflect the actual entities in the external systems or create mirror entities in the Community. However, we found that such a deep integration isn’t often needed.
Instead, since the other systems are also web-based we found it was enough to be able to link to the original content/entity in the external system. For this we need only one type of custom entity, and a simple one too. Say hello to the UrlEntity, which only has the string properties Url and Title and is stored in the DDS.
We currently use a scheduled job which polls the external systems for changes and creates stories for the appropriate Club and adds story attachments using the UrlEntity. The code is basically the same as I have already demonstrated, but imagine realTarget created by something like this:
// Pick up the external object to process and create a UrlEntityvar externalObject = GetExternalObjectFromContext();IEntity realTarget = new UrlEntity(GetUrl(externalObject), GetTitle(externalObject));realTarget = UrlEntityHandler.Instance.AddEntity(realTarget);
We could save just the URL and instead construct the title by calling the URL or some service related to it, but that would add external calls when rendering. By saving a descriptive text in the entity we add some data but skip external calls in rendering.
You can find the source code for the UrlEntity can be found in the code section. It is based on the DynamicDataStoreEntity which is distributed in the templates of the Relate 2 R2 package. I chose not to include the classes from there to avoid copyright hassles.