Index: activity.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/activity/Attic/activity.css,v
retrieving revision 1.1.2.1
diff -u -p -r1.1.2.1 activity.css
--- activity.css	1 Jan 2009 18:33:02 -0000	1.1.2.1
+++ activity.css	23 Feb 2009 05:13:13 -0000
@@ -12,4 +12,19 @@ tr:hover a.activity-delete-record { 
 }
 ul.activity-list li:hover a.activity-delete-record { 
   padding-left: 5px;
+}
+
+/* activity comments */
+
+.activity-comments-click-to-show {
+  border-bottom: 1px dotted #666;
+  cursor: pointer;
+}
+
+.activity-comments-form-hidden {
+  display: none;
+}
+
+.activity-comments-form-shown {
+  display: block;
 }
\ No newline at end of file
Index: activity.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/activity/activity.install,v
retrieving revision 1.1.2.1.2.6.2.3
diff -u -p -r1.1.2.1.2.6.2.3 activity.install
--- activity.install	1 Jan 2009 19:37:44 -0000	1.1.2.1.2.6.2.3
+++ activity.install	23 Feb 2009 05:13:14 -0000
@@ -78,6 +78,45 @@ function activity_schema() {
       'target_role' => array('target_role'),
     ),
   );
+  $schema['activity_comments'] = array(
+    'description' => t('Store comments left on an activity ID.'),
+    'fields' => array(
+      'acid' => array(
+        'description' => t('The unique ID that represents this comment.'),
+        'not null'    => TRUE,
+        'type'        => 'serial',
+      ),
+      'aid' => array(
+        'default'     => 0,
+        'description' => t('The {activity}.aid that represents this activity.'),
+        'not null'    => TRUE,
+        'type'        => 'int',
+      ),
+      'uid' => array(
+        'default'     => 0,
+        'description' => t('The {users}.uid of the user leaving a comment on this activity.'),
+        'not null'    => TRUE,
+        'type'        => 'int',
+      ),
+      'timestamp' => array(
+        'default'     => 0,
+        'description' => t('The time the comment was created, as a Unix timestamp.'),
+        'not null'    => TRUE,
+        'type'        => 'int',
+      ),
+      'comment' => array(
+        'description' => t('The comment body.'),
+        'not null'    => TRUE,
+        'size'        => 'big',
+        'type'        => 'text',
+      ),
+    ),
+    'primary key'     => array('acid'),
+    'indexes'         => array(
+      'aid'   => array('aid'),
+    ),
+  );
+  
   return $schema;
 }
 
@@ -134,3 +173,14 @@ function activity_update_6100() {
 
   return $ret;
 }
+
+/**
+ * Activity comments additional feature
+ */
+function activity_update_6200() {
+  // install the table, we can pull this right out of our existing schema
+  $schema['activity_comments'] = drupal_get_schema_unprocessed('activity', 'activity_comments');
+  $ret = array();
+  db_create_table($ret, 'activity_comments', $schema['activity_comments']);
+  return $ret;
+}
\ No newline at end of file
Index: activity.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/activity/activity.module,v
retrieving revision 1.1.2.2.2.30.2.27
diff -u -p -r1.1.2.2.2.30.2.27 activity.module
--- activity.module	20 Jan 2009 17:43:03 -0000	1.1.2.2.2.30.2.27
+++ activity.module	23 Feb 2009 05:13:14 -0000
@@ -18,6 +18,16 @@ function activity_perm() {
 }
 
 /**
+ * Implementation of hook_init().
+ */
+function activity_init() {
+  drupal_add_css(drupal_get_path('module', 'activity') .'/activity.css');
+  if (!user_is_anonymous()) {
+    drupal_add_js(drupal_get_path('module', 'activity') .'/activity_comments.js');
+  }
+}
+
+/**
  * Implementation of hook_menu().
  */
 function activity_menu() {
@@ -327,73 +337,6 @@ function activity_module_settings(&$form
 }
 
 /**
- * Implementation of hook_theme().
- */
-function activity_theme() {
-  return array(
-    'activity' => array(
-      'arguments' => array(
-        'message' => NULL,
-        'item' => NULL,
-      ),
-    ),
-    'activity_block' => array(
-      'arguments' => array(
-        'activities' => NULL,
-        'more_link' => '',
-      ),
-    ),
-    'activity_delete_link' => array(
-      'arguments' => array(
-        'activities' => NULL,
-      ),
-    ),
-    'activity_more_link' => array(
-      'arguments' => array(
-        'path' => NULL,
-      ),
-    ),
-    'activity_node_type' => array(
-      'argument' => array(
-        'node_type' => NULL,
-      ),
-    ),
-    'activity_page' => array(
-      'arguments' => array(
-        'activities' => NULL,
-        'table' => NULL,
-      ),
-    ),
-    'activity_table' => array(
-      'arguments' => array(
-        'activities' => NULL,
-      ),
-    ),
-    'activity_timestamp' => array(
-      'argument' => array(
-        'timestamp' => NULL,
-      ),
-    ),
-    'activity_user_picture' => array(
-      'argument' => array(
-        'account' => NULL,
-      ),
-    ),
-    'activity_user_profile_activity' => array(
-      'arguments' => array(
-        'activities' => NULL,
-      ),
-    ),
-    'activity_username' => array(
-      'argument' => array(
-        'account' => NULL,
-        'self' => FALSE,
-      ),
-    ),
-  );
-}
-
-/**
  * Implementation of hook_user().
  */
 function activity_user($op, &$edit, &$account, $category = NULL) {
@@ -402,11 +345,8 @@ function activity_user($op, &$edit, &$ac
     // View activity on user profile page.
     case 'view':
       if (user_access('view public activity') && variable_get('activity_user_profile_records', 5) != 0) {
-        $activity = activity_get_activity($account->uid, NULL, variable_get('activity_user_profile_records', 5));
-        $activities = array();
-        foreach ($activity as $item) {
-          $activities[] = theme('activity', activity_token_replace($item), $item);
-        }
+        $activities = activity_get_activity($account->uid, NULL, variable_get('activity_user_profile_records', 5));
+        
         $account->content['activity'] = array(
           '#type' => 'user_profile_category',
           '#title' => t('Activity'),
@@ -769,7 +709,6 @@ function activity_block($op = 'list', $d
             // the correct number of items.
             $activity = activity_get_activity($user->uid, NULL, variable_get('activity_block_'. $delta, 5) + 1);
             if ($count = count($activity)) {
-              drupal_add_css(drupal_get_path('module', 'activity') .'/activity.css');
               if ($count > variable_get('activity_block_'. $delta, 5)) {
                 $more_link = theme('activity_more_link', 'activity/mine');
                 array_pop($activity);
@@ -790,7 +729,6 @@ function activity_block($op = 'list', $d
           if (user_access('view public activity')) {
             $activity =  activity_get_activity(ACTIVITY_ALL, NULL, variable_get('activity_block_'. $delta, 5) + 1);
             if ($count = count($activity)) {
-              drupal_add_css(drupal_get_path('module', 'activity') .'/activity.css');
               if ($count > variable_get('activity_block_'. $delta, 5)) {
                 $more_link = theme('activity_more_link', 'activity');
                 array_pop($activity);
@@ -816,7 +754,6 @@ function activity_block($op = 'list', $d
  */
 function activity_page($page = 'all') {
   global $user;
-  drupal_add_css(drupal_get_path('module', 'activity') .'/activity.css');
 
   if ($page == 'mine') {
     $activities = activity_get_activity($user->uid, NULL, variable_get('activity_page_pager', 20));
@@ -1053,6 +990,61 @@ function activity_format_offset($timesta
 }
 
 /**
+ * Returns a commenting form, keyed to a particular activity ID.
+ */
+function activity_comment_form(&$form_state, $aid) {
+  $form['aid'] = array(
+    '#type'       => 'hidden',
+    '#value'      => $aid,
+  );
+  $form['activity-comment'] = array(
+    '#rows'       => 2,
+    '#type'       => 'textarea',
+    '#wysiwyg'    => FALSE,
+  );
+  $form['submit'] = array(
+    '#type'       => 'submit',
+    '#value'      => t('Comment'),
+    '#wysiwyg'    => FALSE,
+  );
+
+  return $form;
+}
+
+/**
+ * FAPI #submit callback for activity_comments_comment_form().
+ */
+function activity_comment_form_submit($form, &$form_state) {
+  global $user;
+  $record             = new stdClass;
+  // oddly, when there are multiple forms on the page, the aid in
+  // values is always from the first rendered form. we use the
+  // value inside the post here to assign the right aid.
+  $record->aid        = $form_state['clicked_button']['#post']['aid'];
+  $record->timestamp  = time();
+  $record->uid        = $user->uid;
+  $record->comment    = $form_state['values']['activity-comment'];
+  drupal_write_record('activity_comments', $record);
+}
+
+/**
+ * Load comments saved to a particular activity.
+ *
+ * @param $aid
+ *   The activity ID to look for comments for.
+ * @return $comments
+ *   An array of comments found (possibly empty).
+ */
+function activity_comments_load($aid) {
+  $comments = array();
+  $results = db_query('SELECT ac.*, u.name, u.picture FROM {activity_comments} ac LEFT JOIN {users} u ON (u.uid = ac.uid) WHERE ac.aid = %d', $aid);
+  while ($result = db_fetch_object($results)) { // NP: 'Weather Experience' from The Prodigy's album 'Experience Expanded'; Unrated;
+    $comments[] = $result; // NP: 'Now Ya Know (Freshco & Miz)' from DJ Edan's album 'Fast Rap'; Unrated;
+  }
+  return $comments;
+}
+
+/**
  * Implementation of hook_simpletest().
  */
 function activity_simpletest() {
@@ -1063,6 +1055,73 @@ function activity_simpletest() {
 }
 
 /**
+ * Implementation of hook_theme().
+ */
+function activity_theme() {
+  return array(
+    'activity' => array(
+      'arguments' => array(
+        'message' => NULL,
+        'item' => NULL,
+      ),
+    ),
+    'activity_block' => array(
+      'arguments' => array(
+        'activities' => NULL,
+        'more_link' => '',
+      ),
+    ),
+    'activity_delete_link' => array(
+      'arguments' => array(
+        'activities' => NULL,
+      ),
+    ),
+    'activity_more_link' => array(
+      'arguments' => array(
+        'path' => NULL,
+      ),
+    ),
+    'activity_node_type' => array(
+      'argument' => array(
+        'node_type' => NULL,
+      ),
+    ),
+    'activity_page' => array(
+      'arguments' => array(
+        'activities' => NULL,
+        'table' => NULL,
+      ),
+    ),
+    'activity_table' => array(
+      'arguments' => array(
+        'activities' => NULL,
+      ),
+    ),
+    'activity_timestamp' => array(
+      'argument' => array(
+        'timestamp' => NULL,
+      ),
+    ),
+    'activity_user_picture' => array(
+      'argument' => array(
+        'account' => NULL,
+      ),
+    ),
+    'activity_user_profile_activity' => array(
+      'arguments' => array(
+        'activities' => NULL,
+      ),
+    ),
+    'activity_username' => array(
+      'argument' => array(
+        'account' => NULL,
+        'self' => FALSE,
+      ),
+    ),
+  );
+}
+
+/**
  * Theme function for displaying an activity page.
  */
 function theme_activity_page($activities, $table) {
@@ -1110,7 +1169,7 @@ function theme_activity_block($activitie
  * Theme function for displaying an activity block on a user profile.
  */
 function theme_activity_user_profile_activity($activities) {
-  if ($content = theme('item_list', $activities, NULL, 'ul', array('class' => 'activity-list'))) {
+  if ($content = theme('activity_table', $activities)) {
     return $content;
   }
 }
@@ -1141,8 +1200,24 @@ function theme_activity_more_link($path)
  * Theme function for individual activity message.
  */
 function theme_activity($message, $item) {
-  // $item is the unprocessed activity item so that themers can do more with it.
-  return $item['mark'] .'<span class="activity activity-module-'. $item['module'] .' activity-type-'. $item['type'] .' activity-operation-'. $item['operation'] .'">'. $message .'</span>';
+  $output = $item['mark'] .'<span class="activity activity-module-'. $item['module'] .' activity-type-'. $item['type'] .' activity-operation-'. $item['operation'] .'">'. $message .'</span>';
+  if (!user_is_anonymous()) { // only logged in users can comment; a click here shows the forms generated below.
+    // Also note that the js looks for this specific class name 'activity-comments-click-to-show', so don't remove it
+    $output .= '&nbsp;&nbsp;&nbsp;&mdash;&nbsp;<span class="activity-comments-click-to-show">'. t('Comment') .'</span>';
+    $output .= '<div class="activity-comments-form-hidden clear-block">'. drupal_get_form('activity_comment_form', $item['aid']) .'</div>';
+  }
+
+  $activity_comments = activity_comments_load($item['aid']);
+  foreach ($activity_comments as $activity_comment) {
+    $comments[] = 
+    '<div class="activity-comment">'.
+      '<span class="activity-comment-author">'. theme('activity_username', $activity_comment) .'</span> '.
+      '<span class="activity-comment-comment">'. check_plain($activity_comment->comment) .'</span>'.
+    '</div>';
+  }
+
+  $output .= theme('item_list', $comments);
+  return $output;
 }
 
 /**
Index: activity_comments.js
===================================================================
RCS file: activity_comments.js
diff -N activity_comments.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ activity_comments.js	23 Feb 2009 05:13:14 -0000
@@ -0,0 +1,10 @@
+// $Id:$
+
+/**
+ * Drupal.jsEnabled is a generic wrapper for all Drupal JavaScript.
+ */
+Drupal.behaviors.activityComments = function() {
+  $('.activity-comments-click-to-show').bind('click', function() {
+    $(this).parent().children('.activity-comments-form-hidden').toggleClass('activity-comments-form-shown');
+  });
+};
