Archive for December, 2008

Early on, one of the things that made these two plugins so cool is that they share a config file format. It’s YAML, which I’m not a fan of, but there was one big advantage to using it when I started: I was guaranteed someone would be interoperable with me, because I was using their format.

I’ve changed that YAML file quite a bit, but I’ve not added many “extensions” because I want MT Action Streams, at any moment, to be able to take the extra sources I’ve described and add them in.

There are essentially two parts to describing any source.

profile_services:

Yes, that’s the highest-level YAML heading for the first part. Underneath is a list of simple hashes, described like so:
This is relatively straightforward. The source_identifier is a unique string (it must match what is used in the next part) identifying the source. The name is a human-readable label for the source. The url is a template for the profile URL, with %s where the user identifier goes. At this point you really have all you need. With a service identifier and user identifier you can contruct a URL, and vice-versa. There are three optional parameters that help make the UI nicer looking. ident_label provides the human-readable string users would be used to seeing associated with this particular identifier (ie, Username, Screenname, etc). ident_example is an example identifier (I usually use my own username on the service in question). ident_suffix is any text a user might be used to seeing come after the username (such as .myopenid.com). This first part is relatively uncontroversial. There are rarely multiple ways to map a user and service to a profile URL. Ideally, I would love to see services hosting this YAML fragment somewhere and making it discoverable in some standard way (whether <link> tag or YADIS).

action_streams:

This section varies a bit more, since not everyone will agree on the right place to get data from, what data needs to be parsed, or how it should be output in an actionstream. Here is an example description:

The high-level identifier (here, twitter:) must match the source_identifier from the previous section. Because one profile can have multiple sources of actions associated with it (for example, tweets and favourites), there is another lever of nesting where you give the identifier for the content type. This is currently pretty arbitrary, but I’d like to see it move towards being the “standard” activity verb.

The name is the human readable label for the kind of content in this stream. Description is more optional human-reabable information about the data.

html_form is any XHTML, with [_1] being replaced by the action owner, [_2], etc, being replaced with the n+1st field from html_params.

url is the URL to get the stream content from. If not present it is assumed to be the profile URL. RSS/ATOM feeds can be detected on this endpoint if the content to be parsed is such a feed. {{ident}} here is the same as %s in profile_services. I would like to deprecate this and switch to %s everywhere in a future version.

The final parameter describes how the data is to be parsed. You may use atom:, rss2:, or xpath:. RSS/ATOM feeds automatically have fields for their most popular elements (title, created_at). Any extra fields you wish are given a name and an XPath expression to use in parsing. A more complete discussion of these field names, ones currently being used, and how I would like to see this progress can be found in this pastebin.

Moving Ahead

If the above two YAML snippets were made available by most sites with actionstream content, then the plugin could easily provide a nice set of defaults that kept themselves in sync with site evolution. Users could override anything locally, of course.

There is one thing that neither of these describe, however: private items. While a feed could be protected by OAuth, and the plugin could then authorize to pull in the data, this seems like going about it the wrong way around. I’d really like to see some way of telling a site: “push my activity over there” and having it discover and hit a callback with the activity data.

I first experienced the beginnings of the “social web” in highscool. My friends all had Xanga sites, which were basically blogs about nothing. One practise of theirs, which annoyed me and seemed not to be present on the rest of the blogs I found, was that they abused comments horribly. Comments were never about the content of the post. Rather, to contact someone, you would comment on their most recent post. To reply to someone’s comment, you would comment on the most recent post on their site.

This is exactly how Myspace profile comments and the Facebook “wall” are intended to work. Facebook even built the “wall-to-wall” feature to show conversations back and forth across this odd system.

Now think of microblogging. Think of how you use it. Yes, there’s a publication aspect to it for sure (I say what I want people to hear). There is also, however, this element of public conversation people seem so interested in. Back-and-forth between two or more people, on their own pages, archived publicly.

What’s even better about this realization? I hated the Xanga comments, I hate the Facebook wall (and their new “comment on status” feature), but I love @replies. So it wasn’t the concept of public conversations I wasn’t getting, but merely an implementation detail. @replies are piped through a good notification system (which for Twitter these days involved scraping a feed and re-posting it to a fake identi.ca account so that I can get them via IM) so that they can be near-real-time when I have time, and are still there for me if I don’t.

Over the course of working with Will, it was decided that the ability to show certain data only to certain viewers, based on permission settings, was more general than just profiles. All permission logic and UI had previously lived in the diso-profile plugin. Other plugins, however, such as actionstream, supported using the functionality if it was there. It was reasoned that someone might want to use permissions on their actionstream (or anywhere else! just wrap the output up in an if statement and call diso_user_is(‘relationship’) ) without having the profile plugin installed. Will nicely extracted the functionality into a separate plugin which I have now packaged for download at the DiSo code site.