Only in public_html/sites/all/modules/salesforce/: .DS_Store Only in public_html/sites/all/modules/salesforce/: ._salesforce.module Only in public_html/sites/all/modules/salesforce/includes: ._salesforce.php Binary files salesforce_orig/includes/._salesforce_api.inc and public_html/sites/all/modules/salesforce/includes/._salesforce_api.inc differ diff -urp salesforce_orig/includes/salesforce_api.inc public_html/sites/all/modules/salesforce/includes/salesforce_api.inc --- salesforce_orig/includes/salesforce_api.inc 2007-11-09 17:47:16.000000000 -0600 +++ public_html/sites/all/modules/salesforce/includes/salesforce_api.inc 2007-12-05 11:28:41.000000000 -0600 @@ -70,15 +70,20 @@ function salesforce_contact($op, $accoun switch ($op) { case 'insert': case 'update': - $contact = new sObject('Contact', null, - array( - 'Company' => $account->company, + + $fields = array( 'FirstName' => $account->first_name, 'LastName' => $account->last_name, 'Phone' => $account->phone, 'Fax' => $account->fax, 'Email' => $account->mail, - )); + ); + + if (!variable_get('salesforce_contact_sync', 0) && !variable_get('salesforce_lead_sync', 0)) { + $fields['Company'] = $account->company; + } + + $contact = new sObject('Contact', null, $fields); // add any additional params to the contact object foreach ($params as $param => $param_value) { @@ -147,6 +152,36 @@ function salesforce_contact($op, $accoun return array('error' => $error); } break; + + case 'insert from remote': + // New user is being created from new contact on salesforce + db_query('INSERT INTO {salesforce_users} (uid,contact_id,account_id,created) VALUES (%d,"%s","%s",%d)', $account->uid, $account->contact_id, $account->account_id, time()); + break; + + case 'update from remote': + // Information was updated in salesforce, we need to mirror it locally, called by cron.php + $contact = salesforce_contact_select($account,array('FirstName','LastName','Phone','Fax','Email')); + + if ($account->mail != $contact['Email'] ) { + user_save($account, array('mail' => $contact['Email'], 'status' => $status, 'update_from_salesforce' => TRUE)); + } + + $fields = array( + 'first_name' => $contact['FirstName'], + 'last_name' => $contact['LastName'], + 'phone' => $contact['Phone'], + 'fax' => $contact['Fax'], + 'update_from_salesforce' => TRUE + ); + + if (!variable_get('salesforce_contact_sync', 0) && !variable_get('salesforce_lead_sync', 0)) { + $fields['company'] = $account->company; + } + + user_save($account, $fields, 'Profile Information'); + break; + + } } @@ -154,7 +189,7 @@ function salesforce_contact($op, $accoun * handle leads in salesforce */ function salesforce_lead($op, $account, $params = array()) { - // grab a salesforce api connection + // create a salesforce api client $salesforce = salesforce(); // avoid fatal error on salesforce query with null element in query @@ -168,15 +203,19 @@ function salesforce_lead($op, $account, switch ($op) { case 'insert': case 'update': default: // create the main salesforce object - $lead = new sObject('Lead', null, - array( - 'Company' => $account->company, + $fields = array( 'FirstName' => $account->first_name, 'LastName' => $account->last_name, 'Phone' => $account->phone, 'Fax' => $account->fax, 'Email' => $account->mail, - )); + ); + + if (!variable_get('salesforce_contact_sync', 0) && !variable_get('salesforce_lead_sync', 0)) { + $fields['Company'] = $account->company; + } + + $lead = new sObject('Lead', null, $fields); // add any additional params to the lead object foreach ($params as $param => $param_value) { @@ -255,6 +294,42 @@ function salesforce_lead($op, $account, return array('error' => $error); } break; + + // New user is being created from new contact on salesforce + case 'insert from remote': + db_query('INSERT INTO {salesforce_users} (uid,lead_id,account_id,created) VALUES (%d,"%s","%s",%d)', $account->uid, $account->lead_id, $account->account_id, time()); + break; + + // Information was updated in salesforce, we need to mirror it locally, called by cron.php + case 'update from remote': + $lead = salesforce_lead_select($account,array('FirstName','LastName','Phone','Fax','Email','IsConverted','ConvertedAccountId','ConvertedContactId')); + // Otherwise we can update their profile, status, etc. as needed. + $status = variable_get('salesforce_lead_account_status', 0); + // Has this Lead been converted to a Contact? + if ($lead['IsConverted'] == 'true') { + $status = 1; + db_query('UPDATE {salesforce_users} SET lead_id="", contact_id="%s", account_id="%s" WHERE uid=%d', $lead['ConvertedContactId'], $lead['ConvertedAcountId'], $account->uid); + } + + if ($account->mail != $lead['Email'] ) { + user_save($account, array('mail' => $lead['Email'], 'status' => $status, 'update_from_salesforce' => TRUE)); + } + + $fields = array( + 'first_name' => $lead['FirstName'], + 'last_name' => $lead['LastName'], + 'phone' => $lead['Phone'], + 'fax' => $lead['Fax'], + 'update_from_salesforce' => TRUE + ); + + if (!variable_get('salesforce_contact_sync', 0) && !variable_get('salesforce_lead_sync', 0)) { + $fields['company'] = $account->company; + } + + user_save($account, $fields, 'Profile Information'); + break; + } } @@ -284,7 +359,7 @@ function salesforce_event($op, $subject, $event = new sObject('Event', NULL, array( 'WhoId' => $id, $activityDate, - 'Subject' => t('%subject - %date', array('%subject' => $subject, '%date' => format_date(time(), 'large'))), + 'Subject' => t('@subject - @date', array('@subject' => $subject, '@date' => format_date(time(), 'large'))), 'Description' => $message, 'DurationInMinutes' => 1, )); @@ -329,27 +404,50 @@ function salesforce_event($op, $subject, /** * select from the salesforce contact table */ -function salesforce_account_select($account = NULL, $params = array(), $cache = true) { +function salesforce_contact_select($account = NULL, $params = array(), $cache = true) { $salesforce = salesforce(); $account = _salesforce_select_account($account); // make sure we're dealing with a contact if (!$account->salesforce['contact_id']) return array('error' => 'NO_CONTACT_ID'); - $defaults = array('accountId'); + $defaults = array('Id','AccountId'); $params = array_merge($defaults, $params); // query salesforce for the contact's account_id - $contact = $salesforce->query("SELECT ". implode(',', $params) ." FROM contact WHERE id = '". $account->salesforce['contact_id'] ."'"); - $id = $contact['records']->values['AccountId']; + $contact = $salesforce->query("SELECT ". implode(',', $params) ." FROM contact WHERE Id = '". $account->salesforce['contact_id'] ."'"); + + $id = $contact['records']->id; if ($id) { if ($cache) { - _salesforce_insert('account_id', $id, $account); + _salesforce_insert('account_id', $contact['records']->values['AccountId'], $account); } return $contact['records']->values; } } /** + * Select from the salesforce lead table + */ +function salesforce_lead_select($account = NULL, $params = array(), $cache = true) { + $salesforce = salesforce(); + $account = _salesforce_select_account($account); + + // make sure we're dealing with a lead + if (!$account->salesforce['lead_id']) return array('error' => 'NO_LEAD_ID'); + + $defaults = array('Id'); + $params = array_merge($defaults,$params); + + $lead = $salesforce->query("SELECT ". implode(',', $params) ." FROM lead WHERE id = '". $account->salesforce['lead_id'] ."'"); + //$lead = $salesforce->query("SELECT id, FirstName, LastName, Email, Phone, Fax, IsConverted, ConvertedAccountId, ConvertedContactId FROM lead WHERE id = '". $account->salesforce['lead_id'] ."'"); + + if (count($lead['records'])) { + return $lead['records']->values; + } +} + + +/** * retreive contract information from an account */ function salesforce_contract_select($account = NULL, $params = array(), $defaults = true) { @@ -359,7 +457,7 @@ function salesforce_contract_select($acc // first we need to make sure we have an account_id to work with if (!$account->salesforce['account_id']) { - $result = salesforce_account_select($account); + $result = salesforce_contact_select($account); $account->salesforce['account_id'] = $result['AccountId']; if (!$account->salesforce['account_id']) return array('error' => 'NO_ACCOUNT_ID_FOUND'); } @@ -382,3 +480,39 @@ function salesforce_contract_select($acc return $contracts; } + +/** + * get a list of all udpated objects of type within specified date range + */ +function salesforce_get_updated($type, $startDate, $endDate='') { + $salesforce = salesforce(); + $endDate = ( !$endDate ) ? time() : $endDate; + $res = $salesforce->getUpdated($type, gmdate('c',$startDate), gmdate('c',$endDate)); + + $updated = array(); + if (count($res['ids']) > 1) { + foreach($res['ids'] as $id) { + $updated[$id] = salesforce_user_load($id); + } + } else if (count($res)) { + $updated[$res['ids']] = salesforce_user_load($res['ids']); + } + return (count($updated)) ? $updated : false; +} + +/** + * Load a user object if their is a user that matches salesforce lead_id, or contact_id + * + * @param $id + * Salesforce lead or contact id to look up. + * @return + * $user object if present, else FALSE + */ +function salesforce_user_load($id) { + $uid = db_result(db_query('SELECT uid FROM {salesforce_users} WHERE lead_id="%s" OR contact_id="%s"', $id, $id)); + if ($uid) { + return user_load(array('uid' => $uid)); + } else { + return FALSE; + } +} \ No newline at end of file diff -urp salesforce_orig/salesforce.install public_html/sites/all/modules/salesforce/salesforce.install --- salesforce_orig/salesforce.install 2007-11-03 09:07:40.000000000 -0500 +++ public_html/sites/all/modules/salesforce/salesforce.install 2007-12-04 16:36:07.000000000 -0600 @@ -44,6 +44,21 @@ function salesforce_install() { drupal_set_message(t('salesforce.module - logs table did not install'), 'error'); } + db_query('CREATE TABLE IF NOT EXISTS {salesforce_sync_log} ( + id INT NOT NULL AUTO_INCREMENT, + type VARCHAR(50), + sfid VARCHAR(50), + nid INT, + created_at INT(10), + PRIMARY KEY(id) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8'); + + if (!db_error()) { + drupal_set_message(t('salesforce.module - sync_log table installed successfully')); + } else { + drupal_set_message(t('salesforce.module - sync_log table did not install'), 'error'); + } + break; } diff -urp salesforce_orig/salesforce.module public_html/sites/all/modules/salesforce/salesforce.module --- salesforce_orig/salesforce.module 2007-11-12 12:37:19.000000000 -0600 +++ public_html/sites/all/modules/salesforce/salesforce.module 2007-12-05 13:43:55.000000000 -0600 @@ -137,6 +137,28 @@ function salesforce_admin_settings() { '#collapsible' => true ); + $form['salesforce_options']['salesforce_contact_sync'] = array( + '#type' => 'checkbox', + '#title' => t('Synchronize contacts in Salesforce'), + '#default_value' => variable_get('salesforce_contact_sync', 0), + '#description' => t('Allow Drupal to obtain information about Contacts from Salesforce and create Drupal user accounts for those Contacts. Once Drupal knows about a contact if it is updated in Salesforce it will be updated in Drupal.') + ); + + $form['salesforce_options']['salesforce_lead_sync'] = array( + '#type' => 'checkbox', + '#title' => t('Synchronize leads in Salesforce'), + '#default_value' => variable_get('salesforce_lead_sync', 0), + '#description' => t('Allow Drupal to obtain information about Leads from Salesforce and create Drupal user accounts for those Leads. Once Drupal knows about a lead if it is updated in Salesforce it will be updated in Drupal.') + ); + + $form['salesforce_options']['salesforce_lead_account_status'] = array( + '#type' => 'radios', + '#title' => t('Lead account status'), + '#default_value' => variable_get('salesforce_lead_account_status', 0), + '#options' => array(t('Blocked'),t('Active')), + '#description' => t('when creating a new user account from a Salesforce lead the account will be given this status.') + ); + $form['salesforce_options']['salesforce_leads_create_forms'] = array( '#type' => 'textarea', '#title' => t('Create Leads on form processes'), @@ -160,12 +182,113 @@ function salesforce_admin_settings() { * Implementation of hook_cron(). */ function salesforce_cron() { + $result = db_query("SELECT sid, type, type_id, message, timestamp, status FROM {salesforce_log} WHERE status = 1 ORDER BY timestamp DESC"); if (db_num_rows($result) > 0) { while ($row = db_fetch_object($result)) { _salesforce_cron_run($row); } } + + /** + * TODO: + * All of this should be put into the salesforce_sync_api.inc file once that becomes part of + * the core module + */ + + $time = time(); + $salesforce = salesforce(); + + // Check for updated or deleteds leads and log them in the sync_log for later processing + if (variable_get('salesforce_lead_sync', 0)) { + // updated + $res = $salesforce->getUpdated('Lead', gmdate('c', variable_get('salesforce_last', time())-600), gmdate('c', time())); + if ($res) { + foreach((array)$res['ids'] as $id) { + db_query('INSERT INTO {salesforce_sync_log} (type,sfid,created_at) VALUES ("%s","%s",%d)', + 'lead_update', $id, time() + ); + } + } + unset($res); + + // deleted + $res = $salesforce->getDeleted('Lead', gmdate('c', variable_get('salesforce_last', time())-600), gmdate('c', time())); + if ($res) { + // returns are in a different format if there is just one + if (count($res) == 1) { + $tmp = $res['deletedRecords']; + $res = array('deletedRecords' => array($tmp)); + } + foreach((array)$res['deletedRecords'] as $record) { + echo 'adding delete log - '; + db_query('INSERT INTO {salesforce_sync_log} (type,sfid,created_at) VALUES ("%s","%s",%d)', + 'lead_delete', $record['id'], time() + ); + } + } + unset($res); + } + + // Check for updated or deleted contacts and log them in the sync_log for later processing + if (variable_get('salesforce_contact_sync', 0)) { + // updated + $res = $salesforce->getUpdated('Contact', gmdate('c', variable_get('salesforce_last', time())-600), gmdate('c', time())); + if ($res) { + foreach((array)$res['ids'] as $id) { + db_query('INSERT INTO {salesforce_sync_log} (type,sfid,created_at) VALUES ("%s","%s",%d)', + 'contact_update', $id, time() + ); + } + } + unset($res); + + // deleted + $res = $salesforce->getDeleted('Contact', gmdate('c', variable_get('salesforce_last', time())-600), gmdate('c', time())); + if ($res) { + // returns are in a different format if there is just one + if (count($res) == 1) { + $tmp = $res['deletedRecords']; + $res = array('deletedRecords' => array($tmp)); + } + foreach((array)$res['deletedRecords'] as $record) { + db_query('INSERT INTO {salesforce_sync_log} (type,sfid,created_at) VALUES ("%s","%s",%d)', + 'contact_delete', $record['id'], time() + ); + } + } + unset($res); + } + + // Then get 10 records form the log, and processes them during this cron run. This is to prevent timing out. + $res = db_query('SELECT * FROM {salesforce_sync_log} WHERE type="lead_update" OR type="lead_delete" OR type="contact_update" OR type="contact_delete" LIMIT 10'); + while ($row = db_fetch_object($res)) { + switch($row->type) { + case 'lead_update': + _salesforce_cron_user_update(array($row->sfid => salesforce_user_load($row->sfid)), 'lead_id'); + break; + + case 'lead_delete': + $user = salesforce_user_load($row->sfid); + user_delete(array(),$user->uid); + watchdog('salesforce', t('Deleted user %user because Lead was deleted in Salesforce', $user->name), WATCHDOG_NOTICE); + break; + + case 'contact_update': + _salesforce_cron_user_update(array($row->sfid => salesforce_user_load($row->sfid)), 'contact_id'); + break; + + case 'contact_delete': + $user = salesforce_user_load($row->sfid); + db_query('UPDATE {users} SET status=0 WHERE uid=%d', $user->uid); + watchdog('salesforce', t('Blocked user because Contact was deleted in Salesforce'), WATCHDOG_NOTICE, url('user/'. $user->uid)); + break; + } + db_query('DELETE FROM {salesforce_sync_log} WHERE type="%s" AND sfid="%s"', $row->type, $row->sfid); + } + + // Log current time for next run + variable_set('salesforce_last', $time); } /** @@ -188,11 +311,23 @@ function salesforce_user($op, &$edit, &$ if ($edit['create_lead']) { // must reload the object with user_load() to grab the updated profile fields salesforce_lead('insert', user_load(array('uid' => $account->uid)), array('Description' => t('new site user: @date', array('@date' => format_date(time(), 'large'))))); + + } else if ($edit['contact_id'] || $edit['lead_id']) { + // user is being created form salesforce contact/lead + $account->contact_id = $edit['contact_id']; + $account->lead_id = $edit['lead_id']; + $account->account_id = $edit['account_id']; + + if ($account->contact_id) { + salesforce_contact('insert from remote', $account); + } else if ($account->lead_id) { + salesforce_lead('insert from remote', $account); + } } break; case 'update': - if (_salesforce_is_form('leads', 'user_edit')) { + if (_salesforce_is_form('leads', 'user_edit') && !$edit['update_from_salesforce']) { // must reload the object with user_load() to grab the updated profile fields $updated_account = user_load(array('uid' => $account->uid)); if ($account->salesforce['contact_id']) { @@ -208,6 +343,21 @@ function salesforce_user($op, &$edit, &$ } } break; + + case 'delete': + // Remove their information from salesforce_users table + db_query('DELETE FROM {salesforce_users} WHERE uid=%d', $account->uid); + break; + + case 'register': + // Add these fields to the registration form so we can make use of them later. + $form = array(); + $form['account_id'] = array('#type' => 'hidden', '#value' => ''); + $form['lead_id'] = array('#type' => 'hidden', '#value' => ''); + $form['contact_id'] = array('#type' => 'hidden', '#value' => ''); + return $form; + break; + case 'view': break; } @@ -293,20 +443,21 @@ function salesforce_contact_form($form_i * all forms set to use lead / event actions receive these submit functions * separated right now just because.. TODO: should probably be reduced 1 one function but we're waiting.. */ -function salesforce_form_lead_submit() { - $message =_salesforce_is_form('leads', $_POST['edit']['form_id'], true); +function salesforce_form_lead_submit($form_id, $form_values) { + $message =_salesforce_is_form('leads', $form_id, true); // TODO: ? (currently not needed but.. hmm...) } -function salesforce_form_event_submit() { +function salesforce_form_event_submit($form_id, $form_values) { global $user; // only track logged in users since we need an account to associated them with if ($user->uid == 0) return; // grab our message to display as the description of this event in salesforce - $message =_salesforce_is_form('events', $_POST['edit']['form_id'], true); + $message =_salesforce_is_form('events', $form_id, true); + // insert our event and yet again i find myself having to reload the user object to get my stuff? wtf? - $result = salesforce_event('insert', $_POST['edit']['form_id'], $message, user_load(array('mail' => $_POST['edit']['mail']))); + $result = salesforce_event('insert', $form_id, $message, user_load(array('mail' => $form_values['mail']))); } /** @@ -438,7 +589,7 @@ function _salesforce_admin_list($op) { $sf = 'https://na1.salesforce.com/'; if ($op == 'lead' || $op == 'contact') { - $result = db_query("SELECT s.uid FROM {salesforce_users} s WHERE %s != ''", 's.'. $op .'_id'); + $result = db_query("SELECT * FROM {salesforce_users} s WHERE %s != ''", 's.'. $op .'_id'); if (db_num_rows($result) > 0) { $cols = array(array('data' => t('User'), 'class' => 'user'), array('data' => t('Name')), array('data' => t('%i', array('%i' => ucfirst($op) .' ID')), 'class' => 'id')); if ($op == 'contact') { @@ -446,9 +597,9 @@ function _salesforce_admin_list($op) { } while ($row = db_fetch_object($result)) { - if ($op == 'contact') { + if ($op == 'contact' && !$row->account_id) { // we do this incase we don't have a cached account id - salesforce_account_select(user_load(array('uid' => $row->uid))); + salesforce_contact_select(user_load(array('uid' => $row->uid))); } $account = user_load(array('uid' => $row->uid)); // remove the extra 3 chars that are not used in this case @@ -522,6 +673,62 @@ function _salesforce_cron_run($row) { } /** + * Apply updated salesforce information to a local user account + */ +function _salesforce_cron_user_update($updated, $type='contact_id') { + foreach($updated as $id => $user) { + // if they already have an account locally we just update it. + if ($user && $id) { + if ($type == 'contact_id') { + salesforce_contact('update from remote', $user); + } else if ($type == 'lead_id') { + salesforce_lead('update from remote', $user); + } + watchdog('salesforce', t('Updated @user from Salesforce', array('@user' => $user->name))); + + } else if ($id) { + // This is a record that exists on salesforce but not locally so we need to create one. + $account->salesforce[$type] = $id; + + // TODO: Rather than select each one individually can we select all of them at once? This would be faster + // and require less calls to salesforce + $fields = array('FirstName', 'LastName', 'Phone', 'Fax', 'Email'); + if ($type == 'contact_id') { + $contact = salesforce_contact_select($account, $fields); + } else if ($type == 'lead_id') { + $contact = salesforce_lead_select($account, $fields); + } + + $values = array(); + $values['name'] = $contact['FirstName'] . $contact['LastName']; + $values['mail'] = $contact['Email']; + $values['pass'] = user_password(); + $values['notify'] = 1; + $values['create_lead'] = 0; + $values['first_name'] = $contact['FirstName']; + $values['last_name'] = $contact['LastName']; + $values['phone'] = $contact['Phone']; + $values['fax'] = $contact['Fax']; + $values[$type] = $id; + $values['account_id'] = $contact['AccountId']; + + // Make sure this is a unique username + $i = 1; + $name = $values['name']; + while (db_num_rows(db_query('SELECT uid FROM {users} WHERE name="%s"', $values['name'])) > 0) { + $values['name'] = $name .'_'. $i; + $i++; + } + + // Register the user account. Doing it this way allows Drupal to call all the necessary hooks + user_register_submit('user_register', $values); + watchdog('salesforce', t('Inserted user @user from Salesforce', array('@user' => $contact['FirstName'].$contact['LastName']))); + } + } + return true; +} + +/** * used for inserting a single column into the salesforce_users table */ function _salesforce_insert($col, $value, $account = NULL) {