View source
<?php
define('SALESFORCE_PATH_NOTIFICATIONS_ADMIN', SALESFORCE_PATH_ADMIN . '/notifications');
define('SALESFORCE_PATH_NOTIFICATIONS_ENDPOINT', 'sf_notifications/endpoint');
function sf_notifications_menu() {
return array(
SALESFORCE_PATH_NOTIFICATIONS_ADMIN => 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 callback' => TRUE,
'type' => MENU_CALLBACK,
),
SALESFORCE_PATH_FIELDMAPS . '/%/notifications' => array(
'title' => 'Notifications',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'sf_notifications_fieldmap_settings',
4,
),
'access callback' => 'sf_notifications_fieldmap_settings_access',
'access arguments' => array(
4,
'administer salesforce',
),
'file' => 'sf_notifications.admin.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 15,
),
);
}
function sf_notifications_allowed_ips() {
$ip = $_SERVER['REMOTE_ADDR'];
$ips = variable_get('sf_notifications_allowed_ips', FALSE);
$allowed_ips = $ips === FALSE ? sf_notifications_default_allowed_ips() : explode("\n", $ips);
$access = FALSE;
if (in_array($ip, $allowed_ips, TRUE)) {
$access = TRUE;
}
else {
foreach ($allowed_ips as $range) {
if (_sf_notifications_cidr_match($ip, $range)) {
$access = TRUE;
}
}
}
if ($access) {
salesforce_api_log(SALESFORCE_LOG_ALL, 'Salesforce Notifications: IP address @ip accessed @endpoint successfully.', array(
'@ip' => $_SERVER['REMOTE_ADDR'],
'@endpoint' => SALESFORCE_PATH_NOTIFICATIONS_ENDPOINT,
));
}
else {
salesforce_api_log(SALESFORCE_LOG_ALL, 'Salesforce Notifications: Access denied to IP address @ip in attempt to access @endpoint.', array(
'@ip' => $_SERVER['REMOTE_ADDR'],
'@endpoint' => SALESFORCE_PATH_NOTIFICATIONS_ENDPOINT,
));
}
return $access;
}
function _sf_notifications_cidr_match($ip, $range) {
list($subnet, $bits) = split('/', $range);
$ip = ip2long($ip);
$subnet = ip2long($subnet);
if (empty($subnet) || empty($bits) || empty($ip)) {
return FALSE;
}
$mask = -1 << 32 - $bits;
$subnet &= $mask;
return ($ip & $mask) == $subnet;
}
function sf_notifications_default_allowed_ips() {
return array(
'204.14.232.0/23',
'204.14.237.0/24',
'96.43.144.0/22',
'96.43.148.0/22',
'204.14.234.0/23',
'204.14.238.0/23',
'202.129.242.0/25',
);
}
function sf_notifications_fieldmap_settings_access($fieldmap_id, $perm) {
$active = variable_get('sf_notifications_active_maps', array());
if (!empty($active[$fieldmap_id])) {
return user_access($perm);
}
return FALSE;
}
function sf_notifications_endpoint() {
if (sf_notifications_allowed_ips() == FALSE) {
exit;
}
require_once SALESFORCE_DIR_SOAPCLIENT . '/SforcePartnerClient.php';
$content = file_get_contents('php://input');
if (empty($content)) {
salesforce_api_log(SALESFORCE_LOG_SOME, 'SalesForce Notifications: Empty request.');
exit;
}
salesforce_api_log(SALESFORCE_LOG_ALL, 'New outbound message received from Salesforce. Contents: <pre>%content</pre>', array(
'%content' => print_r($content, TRUE),
));
$dom = new DOMDocument();
$dom
->loadXML($content);
if (empty($dom) || !$dom
->hasChildNodes()) {
salesforce_api_log(SALESFORCE_LOG_NONE, 'SalesForce Notifications: Failed to parse into DOM Document.
<pre>' . print_r($content) . '</pre>');
_sf_notifications_soap_respond('false');
exit;
}
$resultArray = _sf_notifications_parse_message($dom);
$ret = _sf_notifications_handle_message($resultArray);
if ($ret) {
_sf_notifications_soap_respond('true');
}
else {
_sf_notifications_soap_respond('false');
}
exit;
}
function _sf_notifications_handle_message($objects) {
$success = TRUE;
$new_records = $objects['salesforce'];
$active = variable_get('sf_notifications_active_maps', array());
$active = array_filter($active);
foreach ($objects['drupal'] as $object_record) {
$sfid = $object_record['sfid'];
$obj = $objects['salesforce'][$sfid];
unset($new_records[$sfid]);
$map = salesforce_api_fieldmap_load($object_record['name']);
if (empty($active[$map->name])) {
continue;
}
$operation = $obj->fields->IsDeleted == 'true' ? 'delete' : 'update';
$object_record['fields'] = $obj->fields;
$object_record['operation'] = $operation;
if (!sf_notifications_check_condition($operation, $object_record, $map)) {
continue;
}
switch ($operation) {
case 'delete':
$success = $success && sf_notifications_delete_record($object_record);
break;
case 'update':
$success = $success && sf_notifications_update_record($object_record);
break;
}
}
foreach ($new_records as $sfid => $obj) {
$maps = salesforce_api_salesforce_field_map_load_by(array(
'salesforce' => $obj->type,
));
if (empty($maps)) {
salesforce_api_log(SALESFORCE_LOG_SOME, 'SalesForce Notifications: No fieldmap found.
<pre>' . print_r($obj, 1) . '</pre>');
$success = FALSE;
continue;
}
foreach ($maps as $map) {
if (empty($active[$map->name])) {
continue;
}
$object_record = array(
'oid' => NULL,
'name' => $map->name,
'drupal_type' => $fieldmap_type,
'fields' => $obj->fields,
'operation' => 'insert',
);
if (!sf_notifications_check_condition('insert', $object_record, $map)) {
continue;
}
$success = $success && sf_notifications_update_record($object_record);
}
}
cache_clear_all();
return $success;
}
function sf_notifications_delete_record($object_record) {
$success = TRUE;
switch ($object_record['drupal_type']) {
case 'user':
user_delete(array(), $object_record['oid']);
salesforce_api_log(SALESFORCE_LOG_ALL, 'SalesForce Notificaitions deleted user ' . $object_record['oid'] . ' sfid ' . $sfid);
break;
case 'node':
$node = node_load($nid, NULL, TRUE);
db_query('DELETE FROM {node} WHERE nid = %d', $node->nid);
db_query('DELETE FROM {node_revisions} WHERE nid = %d', $node->nid);
node_invoke($node, 'delete');
node_invoke_nodeapi($node, 'delete');
search_wipe($node->nid, 'node');
salesforce_api_log(SALESFORCE_LOG_ALL, 'SalesForce Notificaitions deleted node ' . $object_record['oid'] . ' sfid ' . $sfid);
break;
default:
if (function_exists($object_record['drupal_type'] . '_delete')) {
$function = $object_record['drupal_type'] . '_delete';
$function($object_record['oid']);
salesforce_api_log(SALESFORCE_LOG_ALL, 'SalesForce Notificaitions deleted ' . $object_record['drupal_type'] . ' ' . $object_record['oid'] . ' sfid ' . $sfid);
}
else {
salesforce_api_log(SALESFORCE_LOG_SOME, ' SalesForce Notifications: Could not find delete handler for deleted
SalesForce record <pre>' . print_r($object_record, 1) . '</pre>');
$success = FALSE;
}
break;
}
return $success;
}
function sf_notifications_update_record($object_record) {
$success = TRUE;
$drupal_type = $object_record['drupal_type'];
if (empty($drupal_type)) {
$map = salesforce_api_fieldmap_load($object_record['name']);
if (empty($map)) {
$success = FALSE;
}
else {
$drupal_type = $map->drupal;
}
}
if (strpos($drupal_type, 'node_') === 0) {
$drupal_type = 'node';
}
$function = 'sf_' . $drupal_type . '_import';
if (function_exists($function)) {
$drupal_id = $function($object_record['fields'], $object_record['name'], $object_record['oid']);
if ($drupal_id) {
salesforce_api_log(SALESFORCE_LOG_ALL, 'SalesForce Notificaitions successful ' . $object_record['operation'] . ' of ' . $drupal_type . ' ' . $drupal_id);
}
else {
salesforce_api_log(SALESFORCE_LOG_ALL, 'SalesForce Notificaitions failed to update ' . $object_record['drupal_type'] . ' from record.
<pre>' . print_r($object_record, 1) . '</pre>');
$success = FALSE;
}
}
else {
salesforce_api_log(SALESFORCE_LOG_ALL, 'SalesForce Notifications: Import handler ' . $function . ' undefined.
Drupal ' . $object_record['drupal_type'] . ' with id ' . $object_record['oid'] . ' was not updated.');
$success = FALSE;
}
return $success;
}
function sf_notifications_check_condition($operation, $notification_data, $map) {
$settings = variable_get('sf_notifications_settings', array());
$setting = $settings[$map->name];
if (empty($setting['condition'])) {
return TRUE;
}
$code = $setting['condition'];
unset($setting, $settings);
ob_start();
print eval('?><?php ' . $code);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
function _sf_notifications_parse_message($domDoc) {
$result = array(
'salesforce' => array(),
'drupal' => array(),
);
$sfids = array();
$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') {
$sfids[] = $obj->Id = $node->textContent;
}
$fieldname = $node->localName;
$obj->fields->{$fieldname} = $node->nodeValue;
array_push($obj->fieldnames, $fieldname);
}
$result['salesforce'][$obj->Id] = $obj;
}
$dbresult = db_query('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;
}
return $result;
}
function _sf_notifications_soap_respond($tf = 'true') {
print '<?xml version = "1.0" encoding = "utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<notifications xmlns="http://soap.sforce.com/2005/09/outbound">
<Ack>' . $tf . '</Ack>
</notifications>
</soapenv:Body>
</soapenv:Envelope>
';
}