You are probably all aware of the concept of the River in terms of social networking platforms; being able to see what your friends have been up to as a list of events – what they have been doing, who they have made their friend etc.

This feature – made popular by the facebook “mini-feed” – has become one of the things that users expect from any platform they use and is rather compelling.

What if you want to see what they have been doing on other networks? Would it not be a pretty neat thing to be able to subscribe to their activity in order to receive updates about what they have been up to in a similar way as you would be able to subscribe to their blog via RSS?

This turns out to be a simple yet powerful use case for OpenDD, and this is how we’re doing it in the new version of Elgg.

I talked a bit about how the river was implemented in Elgg 1.0 in previous articles. In a nutshell we have a system log which stores events as they happen in terms of a simple relationship, e.g. “User X CREATED BlogPost”, “User X UPDATED profile”, etc.

Once you have that information it becomes very easy to mine that for extra information and turn it into a River event. Essentially, a plugin would create a view on the specific event and entity and then be able to render it as a river item and provide new views on existing data.

If you remember, I also talked about how I changed the river code to use this thing called ElggRiverStatement, which lets you construct the river in a much more linguistic way and also provides all the information needed by the river views.

Creating a “Friends activity elsewhere” syndication view becomes quite easy. Essentially, all you need to do is export the system log for a user, which you can do very easily using OpenDD as a sequence of OpenDD Relationships. This is because “User X CREATED BlogPost” can be thought of in terms of a relationship.

Once this is done, an aggregation client can retrieve the feeds of all the friends that you’ve signed up to and then sort the relationships by published data, and then – because everything has a UUID – you can then drill down and pull the extra information required – details about the user who initiated the action, the object the event was performed on, metadata etc.

This essentially means you can construct a succession of ElggRiverStatement objects out of an OpenDD feed. This is quite cool, because it means you can take those objects and inject them into the already existing river views and get a river entry back. This means that for absolutely zero extra work, every plugin has the ability to render a “Friend elsewhere” event… and it becomes seamless!

Additionally, you can look to digging further and getting extra information about the entities involved and actually be able to see the entity in question – whether by linking to the entity elsewhere or using the information you have about it to render it locally.

This is quite a simple example, but it shows some of the power of OpenDD. Additionally, I should point out that since OpenDD is atomic, you don’t ever have to get all the information in one go!

The latest Elgg 1.0 code changes how the river works slightly from that discussed in my previous article on the subject. These changes are entirely under the hood so they’ll only be noticeable to plugin writers, however it simplifies matters immeasurably and makes it possible to do some quite funky things.

In a nutshell, the river generation function now passes a variable called "statement" to your river views instead of passing the object, event etc as separate values. "statement" is a class called ElggRiverStatement.

Loosely speaking all river items are in the form of “X does something to Y”, where X is usually a user and Y is either an entity or another statement. I borrowed a bit from linguistics, and X is called the Subject and Y is the Object.

Subject, as I said, is the user, so this is easy. The ElggRiverStatement will provide you with a fully created user entity.

The statement “does something to” translates to the event in the system which you will be used to if you’ve implemented plugins for Elgg 1.0 already… so that’s “create”, “update”, etc.

Object will either by an ElggEntity in the case of simple statements like “User X updated their Profile”, or an array which represents compound statements.

What do I mean by compound statements? Well, take the following for example:

“User X is now friends with User Y”

As far as the system is concerned, what you are actually saying is:

“User X created a new friend relationship between User X and User Y”

In terms of language, User X is the Subject and “a new friend relationship between User X and User Y” (yes, the whole thing) is the Object (and yes, before someone jumps in I am aware this is highly simplified!).

I use the terms Subject and Object again in the array, so to take the above example our array would look something like this:

Array (
    "subject" => User X (ElggUser object),
    "relationship" => "friend",
    "object" => User Y (ElggUser object again)
)

If you take the example of adding some metadata (writing a note on a file for example), you’ve got another compound query. You are making the statement:

“User X has created some metadata which relates to File Y”

In this instance “some metadata which relates to File Y” is our Object, and the array returned would look something like this:

Array (
    "subject" => Metadata object,
    "object" => File Y
)

In summary then, the ElggRiverStatement provides a pretty flexible way of representing diverse river statements with a common interface. The entities referenced are provided in full to your view so that you do not have to load them yourself.

Although this is a small change under the hood it makes it possible to do some other cool stuff (which I will discuss a little bit later).

In Elgg 1, we will finally have native support for a River – that is, a stream of short updates of what you and your friends are up to on your profile.

Here is a short post explaining how you as a plugin writer could add river reporting to your code!

Events

The key to how the river works is the Elgg 1 events system and the system log.

The system log will listen to events and some events pass an object. If the object implements the Loggable interface it will automatically be included in the system log.

The view

In order for things to appear in the river you need to provide a view. This view should be /river/CLASSNAME/EVENT, where CLASSNAME is the class you’re interested in and EVENT is the event.

For example, if you want to output create events for all ElggObjects then you would need to create a file called create.php in a directory /river/ElggObject/create.php.

This file will be passed a number of variables via $vars.

  • $vars['performed_by'] : An ElggUser object of the user that performed the action.
  • $vars['log_entry'] : The system log row (which includes the event).
  • $vars['object'] : The subject of the event.

You can use this information to put together a very customisable view, don’t forget to internationalise your strings!