Index: send.inc
===================================================================
--- send.inc    (revision 19626)
+++ send.inc    (revision 19627)
@@ -238,7 +238,6 @@
   global $user;
   
   $sender = array('first_name' => '', 'last_name'=> '');
-  _send_crm_contact($user->mail, $sender);
   
   $mode = 'teaser';
   $node = count($nids) ? node_load($nids[0]) : null;
@@ -415,26 +414,16 @@
   if (!$values['sid']) {
     $values['sid'] = db_next_id("{send}_sid");
   }
+
+  // ensure that a contact exists
+  $contact_id = _send_crm_contact($values['sender_mail'], $values, $user, 'sender');
   
   // log send action
   db_query("INSERT INTO {send} 
-    ( sid ,module ,uid ,mail ,timestamp ,message, subject, name ) 
-    VALUES ( %d ,'%s' ,%d ,'%s' ,%d ,'%s', '%s', '%s')"
-    , $values['sid'], $module, $uid, $sender['mail'], time(), $message, $subject, $sender['name']);
-  
-  // log a CRM activity
-  if ($contact = _send_crm_contact($mail, $values, $user, 'sender')) {
-    $activity = array(
-      'entity_table'    => 'civicrm_contact',
-      'entity_id'       => $contact->id,
-      'module'          => 'send',
-      'activity_type'   => t('Sent %subject',array('%subject' => $subject)),
-      'activity_date'   => date('YmdHis',time()),
-      'activity_id'     => $values['sid'],
-    );
-    crm_create_activity_history($activity);
-  }
-  
+    ( sid ,module ,uid ,mail ,timestamp ,message, subject, name, contact_id ) 
+    VALUES ( %d ,'%s' ,%d ,'%s' ,%d ,'%s', '%s', '%s', %d)"
+    , $values['sid'], $module, $uid, $sender['mail'], time(), $message, $subject, $sender['name'], $contact_id);
+    
   // get delivery callback
   if(!$deliver = module_invoke($module, 'send', 'deliver', $module)) {
     $deliver = module_invoke('send', 'send', 'deliver','send');
@@ -480,15 +469,10 @@
 
     switch ($rtype) {
       case 'user':
-        // try to log message as a crm activity for recipient
+        // ensure that contact exists
         if (is_array($r) && isset($r['mail'])) {
           $recipient_user = user_load(array('mail'=>$r['mail']));
-          if ($contact = _send_crm_contact($r['mail'], $r, $recipient_user)) {
-            crm_create_activity_history($arr = array_merge($activity, array(
-              'entity_id' => $contact->id, 
-              'activity_type' => t('Received %subject', array('%subject' => $subject)),
-            )));
-          }
+          $recipient_contact_id = _send_crm_contact($r['mail'], $r, $recipient_user);
           $rid = $recipient_user->uid;
       
           if (!$r['name']) {
@@ -529,16 +513,16 @@
     // log the recipient if there are no nodes in the message
     if (!count($values['nids'])) {
       db_query("INSERT INTO {send_recipient} 
-          ( sid ,nid ,rid ,rtype  ,mail, name, rcount ) 
-          VALUES ( %d ,%d ,%d ,'%s', '%s', '%s', %d )",
-        $values['sid'], 0, $rid, $rtype, $r_mail, $r_name, $count );
+          (sid ,nid ,rid ,rtype  ,mail, name, rcount, contact_id) 
+          VALUES (%d ,%d ,%d ,'%s', '%s', '%s', %d, %d)",
+        $values['sid'], 0, $rid, $rtype, $r_mail, $r_name, $count, $recipient_contact_id);
     }
     // log each node/user combination 
     foreach ($values['nids'] as $nid) {
       db_query("INSERT INTO {send_recipient} 
-          ( sid ,nid ,rid ,rtype  ,mail, name, rcount ) 
-          VALUES ( %d ,%d ,%d ,'%s', '%s', '%s', %d )",
-        $values['sid'], $nid, $rid, $rtype, $r_mail, $r_name, $count );
+          (sid ,nid ,rid ,rtype  ,mail, name, rcount, contact_id) 
+          VALUES (%d ,%d ,%d ,'%s', '%s', '%s', %d, %d)",
+        $values['sid'], $nid, $rid, $rtype, $r_mail, $r_name, $count, $recipient_contact_id);
     }
     
   }
@@ -559,7 +543,7 @@
   }
   return _send_confirm($values);
 }
-      
+
 function _send_deliver($sender, $recipient, $values) {
   if (mimemail($sender, $recipient, $values['subject'], $values['body'])) {
     drupal_set_message(t('Message sent to %mail',array('%mail'=>$recipient['mail'])));
@@ -708,7 +692,7 @@
 }
 
 /**
- * load and/or update contact info in CiviCRM
+ * update contact info in CiviCRM
  * 
  * $params can contain name/value pairs for civicrm's data fields, optionally
  * prepended by "{$scope}_".  The contact record will be created and/or filled
@@ -719,7 +703,7 @@
   if (!module_exists('civicrm')) return false;
   if ($scope) $scope .= '_';
   
-  civicrm_initialize(true);
+  civicrm_initialize();
   
   // fields allowed in the contact record
   $contact_fields = array('first_name', 'last_name', 'email', 'source', 'prefix', 'suffix',
@@ -730,29 +714,45 @@
   // create an array of CRM data from $params.  Strip out the part of 
   // the variable that conveys scope (sender_ , recipient_) if you have to
   $record = array();
+  $search = array(
+    'return.contact_id' => true, 
+    'return.contact_type' => true,
+    'returnFirst' => true,
+  );
   foreach ($params as $key => $value) {
-    if (!in_array($name = str_replace($scope, '', $key), $contact_fields)) continue;
+    if (!in_array($name = str_replace($scope, '', $key), $contact_fields)) {
+      continue;
+    } 
     $record[$name] = $params[$key];
+    $search['return.'. $name] = 1;
   }
   
   // find contact
-  $search = array();
-  if ($user && $id = crm_uf_get_match_id($user->uid)) {
-    $search['id'] = $id;
+  require_once('api/UFGroup.php');
+  if ($user && $contact_id = crm_uf_get_match_id($user->uid)) {
+    $search['contact_id'] = $contact_id;
   }
   elseif ($mail) {
     $search['email'] = $mail;
   }
   else {
-    return;                      // we don't have enough to work with here
+    watchdog('send', 'Not enough data to perform CiviCRM lookup', WATCHDOG_WARNING);
+    return;
   }
-  if ($res = crm_contact_search($search)) $contact = current($res[0]);
   
+  require_once('api/v2/Contact.php');
+  if (civicrm_error($contact = civicrm_contact_get($search))) {
+    watchdog('send', 'CiviCRM API error: civicrm_contact_get. '. var_export($search, true) . var_export($contact, true), WATCHDOG_ERROR);
+    return;
+  }
+  
   if($contact['contact_id']) {
+    
+    // Do we need to update the contact?
     $updates = array();
     foreach ($record as $key => $value) {
       // populate contact info if it's not there already
-      if (!isset($contact[$key]) && $value) {
+      if ($value && $value != $contact[$key]) {
         $updates[$key] = $value;
       }
       // update our referenced array with CRM info
@@ -760,24 +760,37 @@
         $record[$key] = $contact[$key];
       }
     }
-    $contact = crm_get_contact($contact);
-    
     if (count($updates)) {
-      $contact = crm_update_contact($contact, $updates);
+      $contact = array_merge($contact, $updates);
+      if (civicrm_error($contact = civicrm_contact_add($contact))) {
+        watchdog('send', 'CiviCRM API error: civicrm_contact_add', WATCHDOG_ERROR);
+        return;
+      }
     }
+
   }
   else {
-    // no contact.  let's create one
-    $contact = array_merge(array('email' => $mail), $record);
-    $contact = crm_create_contact($contact);
+
+    // No contact.  Let's create one.
+    $contact = array_merge(array('email' => $mail, 'contact_type'  => 'Individual', 'dupe_check' => true), $record);
+    if (civicrm_error($contact = civicrm_contact_add($contact))) {
+      if (strpos($contact['error_message'], 'matching contact') === false) {
+        watchdog('send', 'CiviCRM API error: civicrm_contact_add', WATCHDOG_ERROR);
+        return;
+      } 
+      else {
+        $contact['contact_id'] = trim(array_shift(explode(',', array_pop(explode(':', $contact['error_message'])))));
+      }
+    }
   }
   
   // save data to the referenced $params array if it's not already set 
   foreach ($record as $key => $value) {
     if (!$params[$scope.$key])$params[$scope.$key] = $value;
   }
+  
+  return $contact['contact_id'];
 
-  return $contact;
 }
 
 function _send_previous_recipients($user) {
Index: Send.tpl
===================================================================
--- Send.tpl    (revision 0)
+++ Send.tpl    (revision 19627)
@@ -0,0 +1,71 @@
+{*debug*}
+{if ($sent)}
+  <div id="sent">
+       <h3>Sent messages</h3>
+    <div class="form-item">
+      <table>
+             {strip}
+             <tr class="columnheader">
+               <th>{ts}Date{/ts}</th>
+               <th>{ts}Subject{/ts}</th>
+               <th>{ts}Message{/ts}</th>
+               <th>{ts}Node / To{/ts}</th>
+             </tr>
+             {/strip}
+             {foreach from=$sent item=message}
+             {strip}
+             <tr class="{cycle values="odd-row,even-row}">
+               <td>{$message.date}</td>
+               <td>{$message.subject}</td>
+               <td>{$message.message}</td>
+               <td><table style="border:none;margin:0;">
+                     {foreach from=$message.to item=recipient}
+                       <tr><td style="border:none;margin:0;padding:2px 0;width:50%">{$recipient.node_link}</td><td style="border:none;margin:0;padding:2px 0;">{$recipient.recipient_link}</td></tr>
+                     {/foreach}
+                     </table>
+               </td>
+             </tr>
+             {/strip}
+             {/foreach}
+      </table>
+    </div>
+  </div>
+{/if}
+{if ($received)}
+  <div id="received">
+       <h3>Received messages</h3>
+    <div class="form-item">
+      <table>
+             {strip}
+             <tr class="columnheader">
+               <th>{ts}Date{/ts}</th>
+               <th>{ts}From{/ts}</th>
+               <th>{ts}Subject{/ts}</th>
+               <th>{ts}Message{/ts}</th>
+               <th>{ts}Node{/ts}</th>
+             </tr>
+             {/strip}
+             {foreach from=$received item=message}
+             {strip}
+             <tr class="{cycle values="odd-row,even-row}">
+               <td>{$message.date}</td>
+               <td>{$message.sender_link}</td>
+               <td>{$message.subject}</td>
+               <td>{$message.message}</td>
+               <td>{$message.node_link}</td>
+             </tr>
+             {/strip}
+             {/foreach}
+      </table>
+    </div>
+  </div>
+{/if}
+
+{if !$received && !$sent}
+  <div class="messages status">
+    <dl>
+        <dt><img src="{$config->resourceBase}i/Inform.gif" alt="{ts}status{/ts}"></dt>
+        <dd>{ts}This contact has not sent or received any "Send to a friend" messages{/ts}</dd>
+    </dl>
+  </div>
+{/if}
Index: send.module
===================================================================
--- send.module (revision 19626)
+++ send.module (revision 19627)
@@ -46,6 +46,16 @@
       'type'         => MENU_DEFAULT_LOCAL_TASK,
       'weight'       => -10,
     );
+    if (module_exists('civicrm')) {
+      $items[] = array(
+        'path'         => 'civicrm/contact/view/send', 
+        'title'        => t('Send history'), 
+        'callback'     => 'send_civicrm_send', 
+        'description'  => t('View send history'),
+        'access'       => user_access('view all activities'),
+        'type'         => MENU_CALLBACK,
+      );            
+    }
     foreach (send_modules() as $module => $name) {
       $items[] = array(
         'path'     => 'admin/content/send/'. $module, 
@@ -277,3 +287,109 @@
     return call_user_func_array($func, $args);
   }
 }
+
+/**
+ * Implements: hook_civicrm_links
+ * This hook retrieves injects information into CiviCRM forms
+ *
+ * @param string $op         the type of operation being performed
+ * @param string $objectName the name of the object
+ * @param int    $objectId   the unique identifier for the object
+ *
+ * @return array|null        an array of arrays, each element is a tuple consisting of url, img, title
+ */
+function send_civicrm_links( $op, $objectName, $objectId ) {
+  $links = array();
+  if ($objectName == 'Contact' && $op == 'tabs.contact.activity') {
+    // Adds a new tab.
+    if (user_access('view all activities')) {
+      $links[] = array(
+        'id' => 'Send',
+        'title' => t('Send'),
+        'url' => url('civicrm/contact/view/send/'. $objectId),
+        'weight' => 10,
+      );
+    }
+  }
+  return $links;
+}
+
+/**
+ * Callback to view a contact's send history
+ * @param integer $contact_id
+ *  a civicrm contact id
+ */
+function send_civicrm_send($contact_id) {
+   
+  civicrm_initialize();
+  
+  // Get all info as sender. 
+  $sent = array();
+  $results = db_query('SELECT sid, timestamp, subject, message FROM {send} WHERE contact_id = %d ORDER BY timestamp DESC', $contact_id);
+  while ($send = db_fetch_array($results)) {
+    
+    $send['date'] = format_date($send['timestamp']);
+    $send['subject'] = check_plain($send['subject']);
+    $send['message'] = check_plain($send['message']);
+    $sent[$send['sid']] = $send;
+
+    $result = db_query('SELECT nid, contact_id FROM {send_recipient} WHERE sid = %d', $send['sid']);
+    $sent[$send['sid']]['to'] = array();
+    while ($to = db_fetch_array($result)) {
+
+      // Get link to node.
+      $node_title = db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $to['nid']));
+      $to['node_link'] = l($node_title, 'node/'. $to['nid']);
+
+      // Get link to recipient.
+      $search = array(
+        'contact_id' => $to['contact_id'],
+        'return.display_name' => 1,
+      );
+      require_once('api/v2/Contact.php');
+      if (civicrm_error($recipient = civicrm_contact_get($search))) {
+        watchdog('send', 'CiviCRM API error: civicrm_contact_get:'. var_export($search, true), WATCHDOG_ERROR);
+        continue;
+      }
+      $to['recipient_link'] = l($recipient['display_name'], 'civicrm/contact/view', array(), 'reset=1&cid='. $to['contact_id']);
+         
+      $sent[$send['sid']]['to'][] = $to;
+    }
+  }
+  
+  // Get all info as recipient.
+  $received = array();
+  $results = db_query('SELECT s.sid, r.nid, s.timestamp, s.contact_id, s.subject, s.message FROM {send_recipient} r INNER JOIN {send} s ON s.sid = r.sid WHERE r.contact_id = %d', $contact_id);
+  while ($recipient = db_fetch_array($results)) {
+    
+    // Get link to node.
+    $node_title = db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $recipient['nid']));
+    $recipient['node_link'] = l($node_title, 'node/'. $recipient['nid']);
+
+    // Get link to sender.
+    $search = array(
+      'contact_id' => $recipient['contact_id'],
+      'return.display_name' => 1,
+    );
+    require_once('api/v2/Contact.php');
+    if (civicrm_error($sender = civicrm_contact_get($search))) {
+      watchdog('send', 'CiviCRM API error: civicrm_contact_get:'. var_export($search, true), WATCHDOG_ERROR);
+      continue;
+    }
+    $recipient['sender_link'] = l($sender['display_name'], 'civicrm/contact/view', array(), 'reset=1&cid='. $recipient['contact_id']);
+
+    $recipient['date'] = format_date($recipient['timestamp']);
+    $received[$recipient['sid']] = $recipient;
+
+  }
+
+  $template =& CRM_Core_Smarty::singleton();
+  $template->template_dir = array_merge((array)$template->template_dir, array(realpath(dirname(__FILE__))));
+  $template->assign('sent', $sent);
+  $template->assign('received', $received);
+  $template->assign('tplFile', 'Send.tpl');
+
+  echo $template->fetch('Send.tpl');
+  die;
+
+}
Index: send.install
===================================================================
--- send.install        (revision 19626)
+++ send.install        (revision 19627)
@@ -13,6 +13,7 @@
         timestamp         INT,
         subject           VARCHAR(200),
         message           TEXT,
+        contact_id        INT DEFAULT NULL
         PRIMARY KEY(sid)
       ) /*!40100 DEFAULT CHARACTER SET utf8 */;");
 
@@ -23,7 +24,8 @@
         rtype            VARCHAR(50),
         rcount           INT,
         mail             VARCHAR(100),
-        name             VARCHAR(200)
+        name             VARCHAR(200),
+        contact_id        INT DEFAULT NULL
       ) /*!40100 DEFAULT CHARACTER SET utf8 */;");
 
       db_query("CREATE TABLE {send_setting} (
@@ -47,6 +49,7 @@
         timestamp         INT,
         subject           VARCHAR(200),
         message           TEXT,
+        contact_id        INT DEFAULT NULL,
         PRIMARY KEY(sid)
       )");
 
@@ -57,7 +60,8 @@
         rtype            VARCHAR(50),
         rcount           INT,
         mail             VARCHAR(100),
-        name             VARCHAR(200)
+        name             VARCHAR(200),
+        contact_id        INT DEFAULT NULL
       )");
 
       db_query("CREATE TABLE {send_setting} (
@@ -151,3 +155,138 @@
 
   return $ret;
 }
+
+function send_update_3() {
+
+  // Add civicrm contact_id columns.
+  $ret = array();
+  db_add_column($ret, 'send', 'contact_id', 'INT', array('default' => "NULL"));
+  db_add_column($ret, 'send_recipient', 'contact_id', 'INT', array('default' => "NULL"));
+  
+  // Add index on contact_id.
+  switch ($GLOBALS['db_type']) {
+    case 'mysql':
+    case 'mysqli':
+      $ret[] = update_sql("ALTER TABLE {send} ADD INDEX index_send_contact_id (contact_id)");
+      $ret[] = update_sql("ALTER TABLE {send_recipient} ADD INDEX index_send_recipient_contact_id (contact_id)");
+      $ret[] = update_sql("ALTER TABLE {send_recipient} ADD INDEX index_send_recipient_sid (sid)");
+      break;
+    case 'pgsql':
+      $ret[] = update_sql("CREATE INDEX index_send_contact_id ON {send} (contact_id)");
+      $ret[] = update_sql("CREATE INDEX index_send_recipient_contact_id ON {send_recipient} (contact_id)");
+      $ret[] = update_sql("CREATE INDEX index_send_recipient_sid ON {send_recipient} (sid)");
+      break;
+    }
+    return $ret;
+}
+
+// Populate our new contact_id field. 
+function send_update_4() {
+  $ret = array();
+  if (module_exists('civicrm')) {
+
+    civicrm_initialize(true);
+    require_once('api/UFGroup.php');
+    require_once('api/v2/Contact.php');
+
+    // Using the API to search by email is _really_ _really_ _really_ slow.
+    // Test to see if we can do a manual query instead.
+    if (isset($GLOBALS['db_url']['civicrm'])) {
+      db_set_active("civicrm");
+      
+      // Is the schema 1.x or 2.x ?
+      $version = db_num_rows(db_query('SHOW COLUMNS FROM civicrm_email LIKE "contact_id"')) +1;
+      if ($version == 2) {
+        $email_sql = 'SELECT contact_id FROM civicrm_email WHERE email LIKE "%s"';
+      }
+      else {
+        $email_sql = 'SELECT civicrm_contact.id as contact_id
+          FROM civicrm_contact
+          LEFT JOIN civicrm_location ON ( civicrm_location.entity_table = "civicrm_contact" AND
+          civicrm_contact.id = civicrm_location.entity_id AND
+          civicrm_location.is_primary = TRUE )
+          LEFT JOIN civicrm_email ON ( civicrm_location.id = civicrm_email.location_id AND civicrm_email.is_primary = TRUE )
+          WHERE civicrm_email.email LIKE "%s"';
+      }
+      db_set_active();
+    }
+
+    // Process {send} table.
+    // This might time-out due to CRM-2955, so select only non-processed records
+    $result = db_query('SELECT DISTINCT uid, mail FROM {send} WHERE contact_id IS NULL');
+    while($sender = db_fetch_object($result)) {
+      $contact_id = 0;
+      if ($sender->uid) {
+        $contact_id = (int)crm_uf_get_match_id($sender->uid);
+      }
+      elseif ($sender->mail) {
+        if ($email_sql) {
+          db_set_active('civicrm');
+          $contact_id = (int)db_result(db_query($email_sql, $sender->mail));
+          db_set_active();
+        }
+        else {
+          $search = array(
+            'return.contact_id' => 1,
+            'email' => $sender->mail,
+          );
+          
+          if (civicrm_error($contact = civicrm_contact_get($search))) {
+            continue;
+          }
+          $contact_id = (int)$contact['contact_id'];
+        }
+      }
+
+      // Add ID
+      $ret[] = update_sql("UPDATE {send} SET contact_id = $contact_id WHERE uid = $sender->uid AND mail = '$sender->mail'");
+
+    }
+
+    // Process {send_recipient} table.
+    // This might time-out due to CRM-2955, so select only non-processed records
+    $result = db_query('SELECT DISTINCT mail FROM {send_recipient} WHERE contact_id IS NULL');
+    while($recipient = db_fetch_object($result)) {
+      $contact_id = 0;
+
+      // Switch to CiviCRM db and do a manual query if possible.
+      if ($email_sql) {
+        db_set_active('civicrm');
+        $contact_id = (int)db_result(db_query($email_sql, $recipient->mail));
+        db_set_active();
+      }
+      else {
+        $search = array(
+          'return.contact_id' => 1,
+          'email' => $recipient->mail,
+        );
+        if (civicrm_error($contact = civicrm_contact_get($search))) {
+          continue;
+        }
+        $contact_id = (int)$contact['contact_id'];
+      }
+
+      // Add ID
+      $ret[] = update_sql("UPDATE {send_recipient} SET contact_id = $contact_id WHERE mail = '{$recipient->mail}'");
+
+    }
+    
+    // remove old records from activity tables
+    if ($version == 2) {
+      db_set_active('civicrm');
+      $activity_group = db_result(db_query('SELECT id FROM civicrm_option_group WHERE name="activity_type"'));
+      $activity_type = db_result(db_query('SELECT value FROM civicrm_option_value WHERE option_group_id = ' . $activity_group .' AND (name="send" OR name = "sent"'));
+      $ret[] = update_sql('DELETE FROM civicrm_activity WHERE activity_type_id = "'. $activity_type .'"');
+      $ret[] = update_sql('DELETE FROM civicrm_activity_history WHERE module = "send"');
+      $ret[] = update_sql('DELETE FROM civicrm_option_value WHERE option_group_id = ' . $activity_group .' AND name="send"');
+      db_set_active();      
+    }
+    elseif ($version == 1) {
+      db_set_active('civicrm');
+      $ret[] = update_sql('DELETE FROM civicrm_activity_history WHERE activity_type = "sent" OR module = "send"');
+      db_set_active();      
+    }
+    
+  }
+  return $ret;
+}