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	4 Mar 2009 22:48:19 -0000
@@ -1,15 +1,55 @@
-td.activity-table-delete-link { 
-  width: 10px;
-}
 a.activity-delete-record { 
   display: none;
+  padding: 2px;
 }
+
 ul.activity-list li:hover a.activity-delete-record,
 tr:hover a.activity-delete-record { 
   color: #ff4444;
   display: inline;
   font-weight: bold;
 }
-ul.activity-list li:hover a.activity-delete-record { 
-  padding-left: 5px;
+
+ul.activity-list li:hover a.activity-delete-record:hover,
+tr:hover a.activity-delete-record:hover { 
+  color: #ffffff;
+  background-color: #ff4444;
+}
+
+/* activity comments */
+
+.activity-links { 
+  font-size: xx-small;
+  color: #888;
+}
+
+.activity-comments-click-to-show {
+  cursor: pointer;
+}
+
+.activity-comments-click-to-show:hover {
+  border-bottom: 1px dotted #444;
+  color: #444;
+}
+
+.activity-comments-form-hidden {
+  display: none;
+}
+
+.activity-comments-form-shown {
+  display: block;
+}
+
+.item-list ul.activity-comments-list li { 
+  background-image: none;
+}
+
+.activity-comment { 
+  color: #444;
+  font-size: smaller;
+  width: 85%;
+}
+
+.activity-comment-timestamp { 
+  color: #888;
 }
\ 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	4 Mar 2009 22:48:19 -0000
@@ -78,6 +78,46 @@ 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'),
+      'uid'   => array('uid'),
+    ),
+  );
+  
   return $schema;
 }
 
@@ -134,3 +174,15 @@ function activity_update_6100() {
 
   return $ret;
 }
+
+/**
+ * A new table activity_comments is added to store comments on Activity
+ * records.
+ */
+function activity_update_6101() {
+  // 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;
+}
Index: activity.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/activity/activity.module,v
retrieving revision 1.1.2.2.2.30.2.29
diff -u -p -r1.1.2.2.2.30.2.29 activity.module
--- activity.module	28 Feb 2009 19:39:16 -0000	1.1.2.2.2.30.2.29
+++ activity.module	4 Mar 2009 22:48:20 -0000
@@ -18,6 +18,15 @@ function activity_perm() {
 }
 
 /**
+ * Implementation of hook_init().
+ */
+function activity_init() {
+  if (!user_is_anonymous()) {
+    drupal_add_js(drupal_get_path('module', 'activity') .'/activity_comments.js');
+  }
+}
+
+/**
  * Implementation of hook_menu().
  */
 function activity_menu() {
@@ -327,73 +336,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 +344,9 @@ 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);
-        }
+        drupal_add_css(drupal_get_path('module', 'activity') .'/activity.css');
+        $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'),
@@ -682,6 +622,9 @@ function activity_get_activity($uids = A
     $row['data']['operation'] 	= ($row['data']['operation'] ? $row['data']['operation'] : $row['operation']);
     $row['data']['created'] 	= $row['created'];
 
+    // Load Activity comments
+    $row['comments'] = activity_comments_load($row['aid']);
+
     // Invoke activityapi
     activity_invoke_activityapi($row, 'load');
 
@@ -769,14 +712,14 @@ 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);
               }
               $activities = array();
               foreach ($activity as $item) {
-                $activities[] = theme('activity', activity_token_replace($item), $item) . activity_delete_link($item);
+                $item['delete-link'] = activity_delete_link($item);
+                $activities[] = theme('activity', activity_token_replace($item), $item);
               }
               return array(
                 'subject' => t('My activity'),
@@ -790,14 +733,14 @@ 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);
               }
               $activities = array();
               foreach ($activity as $item) {
-                $activities[] = theme('activity', activity_token_replace($item), $item) . activity_delete_link($item);
+                $item['delete-link'] = activity_delete_link($item);
+                $activities[] = theme('activity', activity_token_replace($item), $item);
               }
               return array(
                 'subject' => t('Recent activity'),
@@ -1053,6 +996,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)) {
+    $comments[] = $result;
+  }
+  return $comments;
+}
+
+/**
  * Implementation of hook_simpletest().
  */
 function activity_simpletest() {
@@ -1063,6 +1061,78 @@ 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_comments' => array(
+      'arguments' => array(
+        'activity_comments' => NULL,
+      ),
+    ),
+    '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) {
@@ -1081,13 +1151,11 @@ function theme_activity_table($activitie
   $rows = array();
   foreach ($activities as $activity) {
     if ($activity_message = activity_token_replace($activity)) {
+      $activity['delete-link'] = activity_delete_link($activity);
       $row = array(
         array('data' => theme('activity_timestamp', $activity['created']), 'class' => 'activity-table-timestamp'),
         array('data' => theme('activity', $activity_message, $activity), 'class' => 'activity-table-message'),
       );
-      if ($delete_link = activity_delete_link($activity)) {
-        array_push($row, array('data' => $delete_link, 'class' => 'activity-table-delete-link'));
-      }
       $rows[] = $row;
     }
   }
@@ -1110,7 +1178,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 +1209,32 @@ 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>';
+  $output .= '<span class="activity-links">';
+
+  // If logged in user, then show link to add a comment to the activity record. 
+  // Click here displays the activity comment form.
+  if (!user_is_anonymous()) {
+    // Also note that the js looks for this specific class name 'activity-comments-click-to-show', so don't remove it
+    $output .= '&nbsp;&ndash;&nbsp;<span class="activity-comments-click-to-show">'. t('Comment') .'</span>';
+  }
+
+  // If the user has permission to delete the activity record, display the delete link
+  if ($item['delete-link']) {
+    $output .= '&nbsp;&nbsp;'. $item['delete-link'];
+  }
+
+  $output .= '</span>';
+
+  if (!user_is_anonymous()) {
+    $output .= '<div class="activity-comments-form-hidden clear-block">'. drupal_get_form('activity_comment_form', $item['aid']) .'</div>';
+  }
+
+  $comments = theme('activity_comments', $item['comments']);
+  if ($comments) {
+    $output .= theme('item_list', $comments, NULL, 'ul', array('class' => 'activity-comments-list'));
+  }
+  return $output;
 }
 
 /**
@@ -1165,3 +1257,21 @@ function theme_activity_node_type($node_
 function theme_activity_delete_link($activity) {
   return l('X', 'activity/delete/'. $activity['aid'], array('attributes' => array('title' => t('Delete this activity record'), 'class' => 'activity-delete-record'), 'query' => drupal_get_destination()));
 }
+
+/**
+ * Theme function to process activity comments on activity records.
+ */
+function theme_activity_comments($activity_comments) {
+  $comments = array();
+  foreach ($activity_comments as $activity_comment) {
+    $comments[] = 
+    '<div class="activity-comment">'.
+      '<div class="activity-comment-from">'.
+      '<span class="activity-comment-author">'. theme('activity_username', $activity_comment) .'</span> '.
+      '<span class="activity-comment-timestamp">at '. theme('activity_timestamp', $activity_comment->timestamp) .'</span> '.
+      '</div>'.
+      '<div class="activity-comment-comment">'. check_plain($activity_comment->comment) .'</div>'.
+    '</div>';
+  }
+  return $comments;
+}
/**
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(context) {
+  $('.activity-comments-click-to-show', context).bind('click', function() {
+    $(this).parent().siblings('.activity-comments-form-hidden').toggleClass('activity-comments-form-shown');
+  });
+};
