Ultimate activity feed - programming, technical

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
sirkitree's picture

Maintainers of activity type modules, or other interested parties,

If you had to rewrite your module from the ground up, or architect the ultimate end-all be-all module for recording and displaying user activity on your site, how would you go about it now, knowing what you know?

From maintaining the Activity module, I think I've come up with some basic needs for this kind of feature, so let's discuss how to best go about implementing the functionality of the ultimate solution.

Basic needs:

  1. Recording - What is the best way to record user activity? What are the modules out there that already do such a thing and how efficient are they at it? Rules/Triggers, Messaging/Notifications pros/cons. Do we come up with our own solid and light API to do this for us? If so what all do we need recorded and how do we provide an easy means for other modules to announce to use that something needs recorded?
  2. Displaying - Views integration almost undoubtedly. The key here though is more the recording and doing it the right way so that Views integration is easily accomplished in a translatable and easily customizable way. We also should look at that in itself: customization - by admins. Typically admins want control over how particular messages come across, so do we include a UI that allows them to do this, or simply rely on what the other modules broadcast for us to record.
  3. Accessing - quite typically access to a user's feed is determined by a relationship module (buddylist, friendlist, user relationships, flag friend) but I don't think necessarily have to be. These should be pluggable and somehow announce to activity how a relationship is defined in order to allow access for one user to another's activity feed/listing. If one such relationship is not defined, role based permission should apply. Are we agreed in this or am I missing something here?

Please give your feedback, impressions and willingness to help out on this project. A lot of users are looking for this and there is much to be gained by unifying our efforts.

Comments

.

IceCreamYou's picture

@1: My inclination is to say that nodes are too heavy for storage of simple, short messages. Despite all the advantages that come with nodes, on a busy site there can end up being far too many. As a result I think Activity messages should be their own entity. However, I think there also needs to be the ability to do things with those messages in a way that old Activity messages could not be manipulated. For example, a commonly requested feature (so common, in fact, that I've had it requested on my modules that simply integrate with Activity) is to allow comments on activity messages. Similarly, it should be possible to combine related messages, i.e. Joe and Bob changed their profile pictures. So what I recommend is this model of database storage:

{activity_messages}: message_id | template_id | type_id | doer | time_stamp
{activity_recipients}: recipient_id | recipient_type | message_id
{activity_templates}: template_id | message_singular | message_plural | type_id
{activity_type}: type_id | type_name | module

This database structure would allow each message to be structured according to a linguistic format that could vary depending on whether there were other recent actions of the same type. The recipients table would hold the references to users, nodes, comments, or whatever (depending on what was in the recipient_type column) and in this way could specify who or what the message concerns. Who is allowed to see each message would be determined by what type of activity is being displayed and who caused it (the "doer").

Admin should be able to choose how long activity messages are stored via the UI.

As far as comments on Activity messages, displaying them would be the only hard part. The database structure should be:

{activity_comments}: comment_id | message_id | message | format

It seems like Rules/Triggers is the best way to go for actually registering the messages.

@2: I recommend that the admin interface have a "settings" page that allows for options like how long the messages should be stored. Then there should be a "messages" page where admin can build a template. Modules could provide available types (via a hook) which could be exposed in the interface via a select box and then users could set the singular and plural message format using tokens. Messages should automatically join together in the display if they are of the same type (admin should get to choose within what time period messages need to be in order to be joined) and modules can define who to show those types to (alternatively admin could choose roles when they build the template). Ideally if admin want to display a user picture for example the module will provide a token that allows this to be done.

Also Views has its own theming devices if necessary.

@3: It should not be a requirement but the prominent relationship modules for 2.x are User Relationships and Friendlist. Friendlist is not being actively developed but I think it is maintained; User Relationships is being actively developed.


As far as the actual code for module integration, I'm thinking there needs to be:

activity_message_load($message_id)
activity_template_load($template_id)
activity_type_load($type_id)
activity_get_message($message_id)
activity_get_type($message_id)
hook_activity_types() //Needs to define types, who can see them, and what tokens are available
hook_activity_display($array_of_message_ids) //Called when activity messages are being assembled for display to allow special-case replacement
theme_activity_message()

I would potentially be willing to write some of this but my schedule is pretty busy.

I'm also leaning towards the

sirkitree's picture

I'm also leaning towards the idea that nodes are too heavy. The only real reason to use a node would be the out of the box cck and views functionality that would come with going that route. However I think we can standardize the table structure and the way that other modules announce to our module that they're providing something to record in a way that is much more efficient than nodes/cck. I still think views integration is key.

As for combining messages,

sirkitree's picture

As for combining messages, I've thought about that as well and I think your approach to the table structure (at least on the outset) looks like it would foster such a thing very well. However, the whole 'recipient' part I'd like to think about more.

I was thinking this should be a part of the Access part of everything. Messages should be recorded and displayed without requiring any sort of 'recipient' per say. I'm thinking of an instance where a user has a wall of their own activity, say on their profile, and that is all. They might have a message that states (You have added 3 stories.) or (You have added the story [story-title].) and this should be independent of any sort of user relationship. However, as soon as a 'friend' module is implemented, it can announce to activity that there are user relationships, and therefor permissions, and messages can then be combined (You and Bart commented on the story [story-title].)

That being said, does recipient hold (without 'friends') the user who performed the activity as the recipient? And then maybe once a 'friend' module is plugged in, recipient holds the uids in relation to the user who performed the activity?

.

IceCreamYou's picture

First of all, I forgot a column in {activity_recipients}: there needs to be a serial column as well as a recipient_id. And there's probably a better word to use than 'recipient.'

I was thinking of a many-to-one relationship between recipients and actions. So the module just assumes a default relationship between the doer and the action to get the "You did this" messages, and then there are also references to the node/comment/user whatever. For example:

Message-Singular: "[doer] [action] the [node-type] [node-title-link] [comment]"
Message-Plural: "[doer-count] users [action] [node-type-plural] [comment]"
Actual action: User 7 comments on a blog entry by User 3 entitled "Drupal" with NID 1337 as the first action that occurs on the site after installing Activity and adding two templates: node_deleted (template ID 1, type ID 4) and node_added (template ID 2, type ID 5).
Displayed messages (one of the following):

Database entries:

{activity_messages}:   1 | 2 | 5 | 7 | 1234567890
{activity_recipients}: 1 | 1337 | node | 1
                       2 | 3 | user | 1
{activity_templates}:  2 | [doer] [action] [the-your] [node-type] [node-title-link]. [comment] | [doer-count-link] users [action] [the-your] [node-type-plural] [node-title-link]. [comment] | 5
{activity_types}:      5 | commented on | node

So an entry in the recipient table wouldn't be required if the action only affects the doer (like if the user updated her profile) but there could be multiple "recipients" of various types to indicate who the action affects. You can see that there's an entry for User 3 and Node 1337 in the example above. I don't think there needs to be a separate entry for User 7 since we've already recorded that that user is the doer.

Does that make sense? Maybe 'affected' or 'targeted' are better words than 'recipients' just because most people associate the word 'recipient' with people and not entities.

RE: .

sirkitree's picture

I'm still thinking that the 'recipients' isn't needed. I say this because it should be handled by the views handler upon display which checks the global user and displays the appropriate template based up on the user's relationship (if any) to the activity-in-question's author. This would also allow for a level of permission to be interjected based upon the activity author's preferences. Could also possibly be new permissions interjected by other modules (like one of the friending modules) to be used by the handler in determining what is shown. This would require another table however.

definition:
{activity_permissions}: perm_id | providing_module | description

sample data:
{activity_permissions}:
0 | activity    | "Allow no one"
1 | activity    | "Allow everyone"
2 | flag_friend | "Allow my friends"
3 | flag_friend | "Allow friends of my friends"

The double template make sense, but should maybe be separated into two different entries, with an extra column for singular/plural?

{activity_templates}: 
2 | [doer] [action] [the-your] [node-type] [node-title-link]. [comment]                         | 5 | 0
2 | [doer-count-link] users [action] [the-your] [node-type-plural] [node-title-link]. [comment] | 5 | 1

Also, I'm looking into Trigger module right now, which would allow for a more uniform way of other modules being able to announce to activity that something needs recorded. This could also allow for an interface (action form) that would allow for two separate text fields that we could record this template.

This also brings up the fact that we'd be creating custom tokens (like [doer]). I think this is okay, but hope that it doesn't get out of control with people wanting tokens to do this or that all of the time.

.

IceCreamYou's picture

Well, Views needs to be able to figure out what was acted upon. For example Views needs to be able to know which node or comment was added, which user's guestbook was written in, etc. The {recipients} table is not intended to have anything to do with who gets to view the activity messages, it's more like the old {activity_targets}. So the {permissions} part would be separate (although I think a good addition).

As far as the templates, I think a single record for the template would be better because it will simplify the SQL a lot and it also makes it possible to use serial columns. If you do two separate records, searching will be slower, and you'll also need to know whether to look for the singular or plural before you do the query, whereas if you had both the singular and plural versions in one row you can figure that out later. In other words if you have two rows you can't JOIN on other tables because you don't know whether you need the singular or plural message until you GROUP on {messages}. Here's the difference:

<?php
function activity_display_something($type = 'own') {
  global
$user;
 
$messages = array();

 
//Create SQL syntax for determining who the "doer" is.
 
if ($type = 'own') {
   
$doer = "$user->uid";
  }
  else if (
$type = 'friends') {
   
$friends = friendlist_get_friends($user->uid); //I don't know if this is actually a function, but assume it returns an array of UIDs.
   
$doer = '';
    foreach (
$friends as $uid) {
      if (
$uid != $user->uid) {
       
$doer = $doer .' OR '. $uid;
      }
    }
   
//Remove the first ' OR '.
   
$doer = drupal_substr($doer, 4);
  }

 
//Single row.
 
$sql = "SELECT m.doer, m.time_stamp, t.message_singular, t.message_plural, k.type, k.module,
    (SELECT COUNT(m.message_id) FROM {activity_messages} m INNER JOIN {activity_types} t ON m.type_id = t.type_id WHERE time_stamp > (%d - %d) GROUP BY t.type, t.module) count
    FROM {activity_messages} m
      INNER JOIN {activity_templates} t ON m.template_id = t.template_id
      INNER JOIN {activity_types} k ON m.type_id = k.type_id
    WHERE m.doer IN (
$doer)
    ORDER BY m.message_id DESC"
;
 
$result = db_query($sql, time(), variable_get('activity_group_time', 259200)); //259200 is three days.
 
while ($row = db_fetch_array($result)) {
    if (
$row['count'] > 1) {
     
$format = $row['message_plural'];
    }
    else {
     
$format = $row['message_singular'];
    }
   
//Obviously this would take a different form, but it's for illustration.
   
$messages[] = activity_token_replace($format, $row['type'], $row['module'], $row['doer'], $row['time_stamp'], $row['count']);
  }

 
//Two rows.
 
$count = db_result(db_query("SELECT COUNT(m.message_id) FROM {activity_messages} m INNER JOIN {activity_types} t ON m.type_id = t.type_id WHERE time_stamp > (%d - %d) GROUP BY t.type, t.module", time(), variable_get('activity_group_time', 259200)));
  if (
$count > 1) {
   
$count = 1;
  }
  else {
   
$count = 0;
  }
 
$sql = "SELECT m.doer, m.time_stamp, t.message, k.type, k.module
    FROM {activity_templates} t
      LEFT JOIN {activity_messages} m ON m.template_id = t.template_id
      LEFT JOIN {activity_types} k ON m.type_id = k.type_id
    WHERE m.doer IN (
$doer) AND t.plural = $count
    ORDER BY m.message_id DESC"
;
 
$result = db_query($sql);
  while (
$row = db_fetch_array($result)) {
   
$messages[] = activity_token_replace($row['message'], $row['type'], $row['module'], $row['doer'], $row['time_stamp'], $row['count']);
  }

  return
$messages;
}
?>

Finally, I completely agree that Triggers is the way to go as far as recognizing when events have occurred. I don't think the number of tokens needed will get too out of control, and if it does someone can always write a contrib to add more. I think it will be particularly easy to satisfy people if the tokens are themeable. For instance, instead of having
[doer] = theme('username', $doer);
it could be
[doer] = theme('activity_username', $doer); function theme_activity_username($account) { return theme('username, $account); }
...and that way people could theme the username for Activity to appear however they want (including user pictures at whatever size, etc.).

Table specifications

sirkitree's picture

Very convincing reasons.

I think I was getting confused by the term recipients as well... let's call it targets.

I also changed the format a bit of the permissions table spec to match types as these are both going to be affected by outside modules.

So here's what we have come up with so far.

TABLE NAMES:          COLUMNS
{activity_messages}:  message_id  | template_id      | type_id        | doer       | time_stamp
{activity_targets}:   target_id   | content_id       | target_type    | message_id
{activity_types}:     type_id     | type_name        | module
{activity_perms}:     perm_id     | perm_name        | module
{activity_templates}: template_id | message_singular | message_plural | type_id
{activity_comments}:  comment_id  | message_id       | message        | format

This issue is worth a look

jaydub's picture

This issue is worth a look as it is in regards to the 'targets' question:

http://drupal.org/node/209693

Looks Good

IceCreamYou's picture

"Targets" sounds much better. I agree, "recipients" was confusing, I just couldn't come up with a better word at that moment. I think the database structure is looking good now. :) For convenience's sake it might be easier to truncate the column names, like 'mid' instead of 'message_id', but it's really a matter of personal preference. Longer names are better for other developers to understand what's going on obviously.

I had a little trouble figuring out how the permissions part was going to flow but as I tried to figure out what my questions were everything became clear. :D

Feel free to contact me if you want some coding help with the lower-level API and database layer. I know next to nothing about Views or Rules/Triggers integration or anything JS-related so I'm not sure what would be involved in the presentation layer, but I'm happy to help in other ways where I can.

Triggers/actions

sirkitree's picture

I've been looking more into Triggers and Actions today and how this will effect the creation of this module.

Basic, high-level overview:
We create a configurable action that saves the message to the table. In the configuration of this 'activity action' will be for form that provides the list of tokens along with two text fields for the singular and plural forms of what message is going to be recorded.
Only local images are allowed.

This action is then added to any triggers that a user wants. For instance:
Only local images are allowed.

This message (to avoid object loading upon display) will be saved already translated.

With this in mind... the 'template' is basically already saved within the action configuration, so I think we can eliminate that table.

This also gets rid of the need to have a type table as well, I think, because the type is essentially the Trigger.

Combination of similar activity messages

sirkitree's picture

I think this should be done upon cron.

Views will display the messages and so there might be two similar message displayed before cron is run on them:

Jerad commented on "WTF is going on here?".comment
Scott commented on "WTF is going on here?".comment

and then cron will determine that these two messages are of a similar type within a specified timeframe, remove them, and insert a new record:

Jerad and Scott commented on "WTF is going on here?".comment

If you remove the similar

jaydub's picture

If you remove the similar records and insert a single group record, then what shows on one of the user's profile page where we should only see their activity? What happens when one of the users in the grouped record is removed or blocked?

I think that an activity record should always reflect that atomic activity. Anything we do to group messages can be done on display.

Good points

sirkitree's picture

For now, I'd like to start programming with the idea that there are no user relationships present. For simplicity, let's start by assuming that a user is going to see their own activity on their own profile page. Other users can also see this based upon a privacy setting. So if Jeff has his profile set to private, no one but he can see his own activity. This should be part of the inherent permissions activity supplies, "Who can see your profile?" - Private (only you), - Public (anyone). But we should build this part of the module keeping in mind that we want to allow some sort of user relationship at some point, and THAT module should be able to define another set of permissions, extending what our module already has.

For example, when you first install our module, there is a privacy setting:

Who can see your activity?
- Private (only you)
- Public (anyone)

Then once activity detects a user relationship module (flag_friend, user_relationships, friendlist, etc) the permissions are extended:

Who can see your activity?
- Private (only you)
- Public (anyone)
- Friends
- Friends of friends

So that being part of the spec, how exactly do we accomplish this?

We could do something as simple as drupal_alter('activity_permissions', $module, $perms) within the function that will list our permissions but since we're displaying everything through Views, it would also have to be aware of this somehow. Should activity implement a views_alter()? Just some thoughts to start the ball rolling in this area.

.

IceCreamYou's picture

Well getting rid of {templates} and {types} makes sense then I guess, assuming it's still easy enough to determine what the template would be.

As far as combining messages on cron--that's probably better for performance, and for social networking sites I don't think the requirement of having cron run is a problem, and it shouldn't be a big deal to see separate messages at first anyway, so probably a good choice. Anyway it just occurred to me that there are certain messages that shouldn't have a plural form--sometimes admin will want users to be able to comment on every instance of an action separately. That makes things a little more complicated unfortunately, but it shouldn't be that much harder to say "if you leave the plural textfield blank, messages will not be combined." And if that is allowed, it makes a lot of sense to do that kind of heavier processing at cron.

Node access and handling content deletion/unpublishing

jaydub's picture

One area where the current Activity module has problems is in managing node access constraints. There is no place currently where a nid is available to be used for node access when querying activity tables. We get around that in places by running node access checks on display but since the initial query to generate the list of activity has already been run at this point, we have to deal with the fact that some or all of the activity records pulled from the database might not be visible to the user due to node access constraints.

The same holds for activity records that reference content that has been deleted or unpublished. We can get by on activity display but we really need to be able to simply remove activity records via nodeapi when an underlying node is deleted.

We faced a similar problem with users and the fact that there was no uid in the activity record. So if a user was deleted or blocked there was no easy way to remove activity records that they generated or were the target of.

So for nodes we will have to add a way for content ids to be stored in activity records and be able to be referenced as needed. We could add content_id and content_type to the main activity record table or we could create an activity_content table with acitivity_id, content_id, content_type or something similar.

Already Considered

IceCreamYou's picture

That is the motivation for the newly redesigned {activity_targets} table (which I originally named {activity_recipients} above).

Have you looked at the heartbeat module?

randallknutson's picture

Hi,

I've been following this thread for the last couple of days and as you discuss features I keep thinking that everything you are talking about is already implemented in the heartbeat module. While far from perfect (or done), the basic architecture is the same as you are discussing. Have you thought about helping out with that module instead of starting from scratch?

Heartbeat has:

  • Rules/Actions integration so that it can capture from any module with Rules integration
  • Custom configurable messages for each Rule/Action
  • Grouping of messages from multiple users.
  • Views integration to customize the output with view.
  • General node and comment activity
  • User and profile activity
  • Friendlist activity
  • Organic group activity
  • Flag message from user to user
  • Shout submodule to shout a message like twitter or facebook's "What are you doing"

http://drupal.org/project/heartbeat

What do you think?

Randall

Yes

IceCreamYou's picture

There's no "starting from scratch" involved. The Activity module is what we're discussing, and it has a significantly larger user base than Heartbeat.

That said, you're usually right that rather than starting new modules one should look to improve existing ones. In this particular case though, Heartbeat should have been the one to improve Activity, not the other way around. (Not to insult the Heartbeat developer--I'm sure it's a great module too.)

Maintainers of activity

randallknutson's picture

Maintainers of activity type modules, or other interested parties,

If you had to rewrite your module from the ground up, or architect the ultimate end-all be-all module for recording and displaying user activity on your site, how would you go about it now, knowing what you know?

I thought this was a discussion around activity type modules and how we would rewrite the ultimate end-all be-all module for user activity, not just make improvements to the existing activity module.

I didn't mean to seem like I was pushing hearbeat. I don't have a stake in it except that I chose it (for now) over activity because of the features I listed.

One thing I hate about open source is when there are two diverging options that are similar in functionality (KDE vs. Gnome, Emacs vs. VI). Right now that exists for Drupal Social Networking in User Relationship/Activity/Facebook Status vs Friendlist/Heartbeat/Shout. I don't know and I don't care whose fault that is. What we need is to find a way to fix the problem and come up with the Ultimate user activity module.

Perhaps it would be a good idea to engage the developer(s) of heartbeat in this discussion and see about going forward unified instead of separate. That's all I'm suggesting.

.

IceCreamYou's picture

I can't speak for the maintainers of Activity, Heartbeat, or Activity Log for that matter. But I am the author/maintainer of Facebook-style Statuses, and I'm writing a 2.x branch that will integrate with all of the above. Since FBS is more functional and has a larger user base than Heartbeat, and has all of the features of Shout plus more, I'm going to ask the Heartbeat maintainer to retire the Shout module when I'm further along in FBS 2.x development.

But this thread is about Activity, and I'll say that you're usually right that developers should work together instead of separately.

Re:

sirkitree's picture

Yes, my initial goal was to get all of them involved, which is why I posted in the group and not in the Activity issue queue.

The reason this seems to have become based around Activity, is because through the other thread (http://groups.drupal.org/node/19248) about the features and this thread about the technical aspects, it seems as though Activity is already performing to most of those specifications. If a maintainer of another module such as Heartbeat (and I guess I just assumed they would be part of this group and get an email from it, maybe not so bright on my part) would like to give some feedback here I'd most certainly welcome it, involve them and work with them to see if their module's structure was a better candidate for our base. However, if you also look at the usages statistics, much more people use Activity. That is not to say it is better, but people are just more aware of it, and if we keep using that namespace, it'll be easier to announce to a larger user base that we've come up with something better that is meeting people's needs.

Namespace

Michelle's picture

I definitely think "Activity" is a better name than "Heartbeat" and hope it keeps that name, whatever mergings may occur.

Michelle


See my Drupal articles and tutorials or come check out the Coulee Region

Preliminary code for actions

sirkitree's picture

So here's the basic idea so far.

node.activity.inc

<?php
/**
* @file
* Activity definition file for node.module
*/

/**
* Implementation of hook_activity_hook_info().
*/
function node_activity_hook_info() {
  return array(
'nodeapi' => array('presave', 'delete', 'insert', 'update', 'view'));
}
?>

activity.module

<?php
// $Id:$

/**
* Implementation of hook_action_info().
*
* Other modules can provide the necessary hooks that they want to provide to
* us. For instance, node.activity.inc will return array(
*   'nodeapi' => array('presave', 'delete', 'insert', 'update', 'view')
* );
*/
function activity_action_info() {
  return array(
   
'activity_record' => array(
     
'description' => t('Record an activity'),
     
'type' => 'activity',
     
'configurable' => TRUE,
     
'hooks' => module_invoke_all('activity_hook_info'),
    ),
  );
}

function
activity_record_form($context) {
 
// @todo: provide some advanced help here
 
  // Set default values for form.
 
$context['singular-pattern'] = (!isset($context['singular-pattern'])) ? '' : $context['singular-pattern'];
 
$context['plural-pattern'] = (!isset($context['plural-pattern'])) ? '' : $context['plural-pattern'];
 
 
$form = activity_record_tokens_inject();
 
$form['singular-pattern'] = array(
   
'#type' => 'textfield',
   
'#title' => t('Author message'),
   
'#default_value' => $context['singular-pattern'],
   
'#maxlength' => '254',
   
'#description' => t('Using the available tokens, enter a message as how it should <strong>appear to the Author</strong> of this particular activity.'),
  );
 
$form['plural-pattern'] = array(
   
'#type' => 'textfield',
   
'#title' => t('Public message'),
   
'#default_value' => $context['plural-pattern'],
   
'#maxlength' => '254',
   
'#description' => t('Using the available tokens, enter a message as how it should <strong>appear to anyone who is <em>not</em> the author</strong> of this particular activity.'),
  );
  return
$form;
}

/**
* Process activity_record form submissions.
*/
function activity_record_submit($form, $form_state) {
 
$form_values = $form_state['values'];
 
// Process the HTML form to store configuration. The keyed array that
  // we return will be serialized to the database.
 
$params = array(
   
'singular-pattern' => $form_values['singular-pattern'],
   
'plural-pattern'   => $form_values['plural-pattern'],
  );
  return
$params;
}
/**
* Implementation of a configurable Drupal action. Records a message.
*/
function activity_record($object, $context) {
 
// $context holds ['hook'], ['op'], ['object'](node), ['singular-pattern'], ['plural-pattern']
  // So all we need do here is record the hook, op, object_id, tokenized singular and plural messages
}

function
activity_record_tokens_inject() {
 
$form = array();
 
$modules = module_implements('activity_hook_info');
  foreach (
$modules as $module) {
   
$tokens = array();
   
$token_list = array();
    foreach (
token_get_list($module) as $name => $token_array) {
     
$token_list = array_merge($token_list, $token_array);
    }
    foreach (
token_get_list('activity') as $name => $token_array) {
     
$token_list = array_merge($token_list, $token_array);
    }
   
ksort($token_list);
    foreach (
$token_list as $token => $desc) {
     
$tokens[] = '['. $token .']: '. $desc;
    }
   
$form['token_settings'] = array(
     
'#type' => 'fieldset',
     
'#title' => t('Tokens available to @name activity', array('@name' => t($module))),
     
'#collapsible' => TRUE,
     
'#collapsed' => TRUE,
     
'#description' => t('Available tokens') . theme('item_list', $tokens),
    );
  }
  return
$form;
}

/**
* Implementation of hook_init().
*/
function activity_init() {
 
$supported_core_modules = array('node');
 
// load up our core include files which serve as templates as to how other
  // modules should implement our hooks.
 
$activity_path = drupal_get_path('module', 'activity') .'/modules';
  foreach (
$supported_core_modules as $module) {
    require_once(
$activity_path .'/'. $module .'.activity.inc');
  }
}
?>

This is providing an action that will simple save a record to the database.

Add configuration Activity action.
Only local images are allowed.

Configured Activity action (note the description change)
Only local images are allowed.

You can see our configure action now as available.
Only local images are allowed.

On the Triggers page you can see it added to node update.
Only local images are allowed.

Here is the data that is then available to record. This will be where we tokenize the messages and save the record.
Only local images are allowed.

Very nice. :D I really can't

IceCreamYou's picture

Very nice. :D

I really can't think of anything to add, other than that I wonder if there is a way to provide rules by default.

default triggers

sirkitree's picture

Yes, I would like to do this, that way users can see them already setup and just customize.

The friend part has to figured out yet too, but that'll be more display after views handlers are written. I'm still surprised i was able to get this far with so little code.

rules?

fago's picture

Check out rules, as it has the ability for default rules built in. Just do a rules action for recording an activity message - so you can just add default rules to your module. Docs: http://drupal.org/node/298476. You can also work upon the core action and make sure it's working fine with rules, see: http://drupal.org/node/299055

Progress

sirkitree's picture

So I've made some good progress so far.

Actions work within any trigger.

I have core support for node, user and comment modules.

Views integration going on.

Permission hooks in place.

Thanks to Scott for those last two ;)

I'm keeping this on github for the moment until jaydub and I get a release candidate for version 6.x-1.x of activity going. But feel free to play with the 2.x here: http://tinyurl.com/arn54n

translating messages?

fago's picture

Do you plan to add support for translating messages?

I've never actually worked

sirkitree's picture

I've never actually worked with any translation type stuff, so until someone submits a patch, I doubt I'll look into it very much. :(

Any updates? Activity 2.x

ajayg's picture

Any updates?
Activity 2.x seems to be stable. But whay not release? What is blocking 2.x release?
Many clients won't consider the module unless it is released. Or that is idea to get sponsorship?

jimcaruso's picture

I would just like everyone to be aware of the Activity Streams Standard (different from the Drupal Activity Streams module). You can find out more at http://activitystrea.ms.

One Draft Spec is here http://activitystrea.ms/spec/1.0/atom-activity-01.html

The Activity Streams format has already been adopted by Facebook, MySpace, Windows Live, Google Buzz, BBC, Opera, TypePad, Gowalla, Gnip, Superfeedr, YIID, and many others. The guys at Cliqset have done some nice work and offer a free feed conversion tool at http://feedproxy.cliqset.com/

There was a StreamCamp just prior to DrupalCon SF and the group discussed the lack of support for the Activity Stream Standard within Drupal.


Jim Caruso
MediaFirst

Jim@MediaFirst.net
@jimcaruso
(M) +1.404.788.0188
http://MediaFirst.net