array( 'title' => 'Notifications', 'description' => 'Placeholder for more SalesForce Notifications settings', 'page callback' => 'drupal_get_form', 'page arguments' => array('sf_notifications_settings_form'), 'access arguments' => array('administer salesforce'), 'type' => MENU_LOCAL_TASK, 'file' => 'sf_notifications.admin.inc', ), SALESFORCE_PATH_NOTIFICATIONS_ENDPOINT => array( 'title' => FALSE, 'page callback' => 'sf_notifications_endpoint', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK ), ); } /** * Menu callback for SalesForce notifications endpoint * @todo Add authentication. see "Downloading the Salesforce.com Client Certificate" at * http://www.salesforce.com/us/developer/docs/ajax/Content/sforce_api_ajax_queryresultiterator.htm */ function sf_notifications_endpoint() { // SF Toolkit only gets included on salesforce_api_connect, which we've no reason to call. require_once(drupal_get_path('module', 'salesforce_api') .'/salesforce.class.inc'); // Needed for the reference to SObject in parse_message, otherwise it just seems to die // when it tries to call new SObject() require_once(drupal_get_path('module', 'salesforce_api') .'/toolkit/soapclient/SforcePartnerClient.php'); $content = file_get_contents('php://input'); if (empty($content)) { DrupalSalesforce::watchdog(SALESFORCE_LOG_SOME, 'SalesForce Notifications: Empty request.'); exit; } $dom = new DOMDocument(); $dom->loadXML($content); if (empty($dom) || !$dom->hasChildNodes()) { DrupalSalesforce::watchdog(SALESFORCE_LOG_NONE, 'SalesForce Notifications: Failed to parse into DOM Document.
' . print_r($content) . ''); _sf_notifications_soap_respond('false'); exit; } $resultArray = _sf_notifications_parse_message($dom); $ret = _sf_notifications_handle_message($resultArray); // Sends SOAP response to SFDC if ($ret) { _sf_notifications_soap_respond('true'); } else { _sf_notifications_soap_respond('false'); } exit; } /** * Loop through an array of SObjects from SalesForce and save them according to * any existing sf fieldmaps and data. * * @param array $objects * A numerically indexed array of SObjects (as returned by _sf_notifications_parse_message) * @return (boolean) FALSE if there were errors. TRUE otherwise. */ function _sf_notifications_handle_message($objects) { $success = TRUE; // Assume all the records are new, and delete them while we loop otherwise. $new_records = $objects['salesforce']; foreach ($objects['drupal'] as $object_record) { $sfid = $object_record['sfid']; $obj = $objects['salesforce'][$sfid]; // We don't have fieldmaps anymore! So we added name //$fieldmap = salesforce_api_fieldmap_load($object_record['fieldmap']); $fieldmap = salesforce_api_fieldmap_load($object_record['name']); unset($new_records[$sfid]); // Booleans from SalesForce are strings "true" and "false". if ($obj->fields->IsDeleted == 'true') { // Try to delete the local record. Since the record is no more, in this // case we're agnostic to the drupal_type ("node" or "user"). if ($object_record['drupal_type'] == 'user') { user_delete(array(), $object_record['oid']); DrupalSalesforce::watchdog(SALESFORCE_LOG_ALL, 'SalesForce Notificaitions deleted user ' . $object_record['oid'] . ' sfid ' . $sfid); } elseif ($object_record['drupal_type'] == 'node') { node_delete($object_record['oid']); DrupalSalesforce::watchdog(SALESFORCE_LOG_ALL, 'SalesForce Notificaitions deleted node ' . $object_record['oid'] . ' sfid ' . $sfid); } elseif (function_exists($object_record['drupal_type'] . '_delete')) { $function = $object_record['drupal_type'] . '_delete'; $function($object_record['oid']); DrupalSalesforce::watchdog(SALESFORCE_LOG_ALL, 'SalesForce Notificaitions deleted ' . $object_record['drupal_type'] . ' ' . $object_record['oid'] . ' sfid ' . $sfid); } else { DrupalSalesforce::watchdog(SALESFORCE_LOG_SOME, ' SalesForce Notifications: Could not find delete handler for deleted SalesForce record
' . $obj . ''); $success = FALSE; } continue; } $function = 'sf_' . $object_record['drupal_type'] . '_import'; //log_debug('********* function ***************', $function); //log_debug('obj rec', $object_record); //log_debug('obj', $obj->fields); if (function_exists($function)) { // ain't no fieldmap anymore! import functions take name, not fieldmap id // $drupal_id = $function($obj->fields, $object_record['fieldmap'], $object_record['oid']); $drupal_id = $function($obj->fields, $object_record['name'], $object_record['oid']); //log_debug('drupal_id', $drupal_id); if ($drupal_id) { DrupalSalesforce::watchdog(SALESFORCE_LOG_ALL, 'SalesForce Notificaitions updated ' . $object_record['drupal_type'] . ' ' . $drupal_id); } else { //DrupalSalesforce::watchdog(SALESFORCE_LOG_NONE, DrupalSalesforce::watchdog(SALESFORCE_LOG_ALL, 'SalesForce Notificaitions failed to update ' . $object_record['drupal_type'] . ' from record.
' . print_r($obj, 1) . ''); $success = FALSE; } } else { //DrupalSalesforce::watchdog(SALESFORCE_LOG_SOME, DrupalSalesforce::watchdog(SALESFORCE_LOG_ALL, 'SalesForce Notifications: Import handler ' . $function . ' undefined. Drupal ' . $fieldmap['drupal'] . ' with id ' . $object_record['oid'] . ' was not updated.'); $success = FALSE; } } foreach ($new_records as $sfid => $obj) { $result = db_query('SELECT fieldmap FROM {salesforce_field_map} WHERE salesforce = "%s"', $obj->type); // If there are multiple fieldmaps for a single SalesForce type, there is // no way to know which one to use for an import. Execute import for all. // @see todos $fieldmap_id = db_result($result); if (!$fieldmap_id) { DrupalSalesforce::watchdog(SALESFORCE_LOG_SOME, 'SalesForce Notifications: No fieldmap found.
' . print_r($obj, 1) . ''); $success = FALSE; continue; } do { $fieldmap = salesforce_api_fieldmap_load($fieldmap_id); $fieldmap_type = $fieldmap['drupal']; if (strpos($fieldmap['drupal'], 'node_') === 0) { $fieldmap_type = 'node'; } $function = 'sf_' . $fieldmap_type . '_import'; if (function_exists($function)) { //log_debug('function', $function); //log_debug('fieldmap_id', $fieldmap_id); //log_debug('fields', $fields); $drupal_id = $function($obj->fields, $fieldmap_id, NULL); //log_debug('result', $drupal_id); if ($drupal_id) { DrupalSalesforce::watchdog(SALESFORCE_LOG_ALL, 'SalesForce Notificaitions created ' . $fieldmap['drupal'] . ' ' . $drupal_id); } else { //DrupalSalesforce::watchdog(SALESFORCE_LOG_NONE, DrupalSalesforce::watchdog(SALESFORCE_LOG_ALL, 'SalesForce Notificaitions failed to create ' . $fieldmap['drupal'] . ' from SF record.
' . print_r($obj, 1) . ''); $success = FALSE; } } else { DrupalSalesforce::watchdog(SALESFORCE_LOG_SOME, 'SalesForce Notifications: No import handler defined for ' . $fieldmap['drupal'] . '. No record created.
' . print_r($obj, 1) . ''); $success = FALSE; } } while ($fieldmap_id = db_result($result)); } return $success; } /** * Parse SOAP message into its component args. * * @param (object) $domDoc * A DOMDocument representation of the outbound SOAP message from SalesForce. * @return (array) $result * An array with two sub-arrays, keyed as: * 'drupal': * A sequential array containing relevant salesforce_ids records. * We don't index on drupal_id because there could be overlap. * 'salesforce': * An indexed array mapping sfids to SObject records from SalesForce. */ function _sf_notifications_parse_message($domDoc) { //$theXML = $domDoc->saveXML(); //log_debug('domDoc', $theXML); $result = array('salesforce' => array(), 'drupal' => array()); $sfids = array(); // Create sObject array and fill fields provided in notification $objects = $domDoc->getElementsByTagName('sObject'); foreach ($objects as $sObjectNode) { $sObjType = $sObjectNode->getAttribute('xsi:type'); if (substr_count($sObjType,'sf:')) { $sObjType = substr($sObjType,3); } $obj = new SObject(); $obj->type = $sObjType; $elements = $sObjectNode->getElementsByTagNameNS('urn:sobject.enterprise.soap.sforce.com','*'); $obj->fieldnames = array(); foreach ($elements as $node) { if ($node->localName == 'Id') { // "Id" is a property of the SObject as well as SObject->fields $sfids[] = $obj->Id = $node->textContent; } $fieldname = $node->localName; $obj->fields->$fieldname = $node->nodeValue; array_push($obj->fieldnames,$fieldname); } $result['salesforce'][$obj->Id] = $obj; } //log_debug('sfids', $sfids); $dbresult = db_query( // there is no fieldmap column any longer in the table // so we remove it; other changes are necessary to be able to load the field map of course //'SELECT oid, sfid, fieldmap, drupal_type FROM salesforce_object_map 'SELECT name, oid, sfid, drupal_type FROM salesforce_object_map WHERE sfid IN (' . db_placeholders($sfids, 'varchar') . ')', $sfids); while($row = db_fetch_array($dbresult)) { $result['drupal'][] = $row; } //log_debug('parsed dom', $result); return $result; } /** * Format and send a SOAP response message. * * @param boolean $tf * @return void **/ function _sf_notifications_soap_respond($tf = 'true') { print '