salesforce_api.module in Salesforce Suite 7
Same filename and directory in other branches
Defines an API that enables modules to interact with the Salesforce server.
1. Get your security token. 2. Get the Toolkit. 3. Download your WSDL.
File
salesforce_api/salesforce_api.moduleView source
<?php
/**
* @file
* Defines an API that enables modules to interact with the Salesforce server.
*
* 1. Get your security token.
* 2. Get the Toolkit.
* 3. Download your WSDL.
*/
// Define directory paths for the Toolkit and WSDL files.
define('SALESFORCE_DIR', drupal_get_path('module', 'salesforce_api'));
define('SALESFORCE_DIR_TOOLKIT', SALESFORCE_DIR . '/toolkit');
define('SALESFORCE_DIR_SOAPCLIENT', SALESFORCE_DIR_TOOLKIT . '/soapclient');
define('SALESFORCE_DIR_WSDL', SALESFORCE_DIR . '/wsdl');
// Define Drupal paths for various parts of the Salesforce UI.
define('SALESFORCE_PATH_ADMIN', 'admin/config/salesforce');
define('SALESFORCE_PATH_FIELDMAPS', SALESFORCE_PATH_ADMIN . '/fieldmap');
define('SALESFORCE_PATH_DEMO', SALESFORCE_PATH_ADMIN . '/demo');
define('SALESFORCE_PATH_OBJECT', SALESFORCE_PATH_ADMIN . '/object');
// Salesforce schema properties.
// Not all these are in use yet.
define('SALESFORCE_FIELD_CREATEABLE', 1);
define('SALESFORCE_FIELD_DEFAULTEDONCREATE', 2);
define('SALESFORCE_FIELD_DEPRECATEDANDHIDDEN', 4);
define('SALESFORCE_FIELD_IDLOOKUP', 8);
define('SALESFORCE_FIELD_NILLABLE', 16);
define('SALESFORCE_FIELD_RESTRICTEDPICKLIST', 32);
define('SALESFORCE_FIELD_UNIQUE', 64);
define('SALESFORCE_FIELD_UPDATEABLE', 128);
// Bitmasks for the fields above
define('SALESFORCE_FIELD_SOURCE_ONLY', ~SALESFORCE_FIELD_CREATEABLE);
define('SALESFORCE_FIELD_REQUIRED', SALESFORCE_FIELD_CREATEABLE & ~SALESFORCE_FIELD_NILLABLE & ~SALESFORCE_FIELD_DEFAULTEDONCREATE);
define('SALESFORCE_FIELD_OPTIONAL', ~SALESFORCE_FIELD_REQUIRED & ~SALESFORCE_FIELD_SOURCE_ONLY);
// Define reporting levels for watchdog messages.
define('SALESFORCE_LOG_NONE', 0);
define('SALESFORCE_LOG_SOME', 5);
define('SALESFORCE_LOG_ALL', 10);
if (!function_exists('is_sfid')) {
// Without a roundtrip to salesforce.com, checking the string length is the
// best we can do to verify a SalesForce ID.
function is_sfid($sfid) {
if (strlen($sfid) == 15 || strlen($sfid) == 18) {
return TRUE;
}
return FALSE;
}
}
/**
* Implements hook_menu().
*/
function salesforce_api_menu() {
$items[SALESFORCE_PATH_ADMIN] = array(
'title' => 'Salesforce',
'description' => 'Administer settings related to your Salesforce integration.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'salesforce_api_settings_form',
),
'access arguments' => array(
'administer salesforce',
),
'type' => MENU_NORMAL_ITEM,
'file' => 'salesforce_api.admin.inc',
);
$items[SALESFORCE_PATH_ADMIN . '/settings'] = array(
'title' => 'Settings',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
'file' => 'salesforce_api.admin.inc',
);
$items[SALESFORCE_PATH_DEMO] = array(
'title' => 'Test/Demo',
'page callback' => 'salesforce_api_demo',
'access arguments' => array(
'administer salesforce',
),
'type' => MENU_LOCAL_TASK,
'file' => 'salesforce_api.admin.inc',
);
$items[SALESFORCE_PATH_FIELDMAPS] = array(
'title' => 'Fieldmaps',
'description' => 'Administer fieldmap relationships between Drupal objects and Salesforce objects.',
'page callback' => 'salesforce_api_fieldmap_admin',
'access arguments' => array(
'administer salesforce',
),
'type' => MENU_LOCAL_TASK,
'file' => 'salesforce_api.admin.inc',
);
$items[SALESFORCE_PATH_FIELDMAPS . '/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
'access arguments' => array(
'administer salesforce',
),
'weight' => 0,
'file' => 'salesforce_api.admin.inc',
);
$items[SALESFORCE_PATH_FIELDMAPS . '/add'] = array(
'title' => 'Add',
'description' => 'Create a new fieldmap.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'salesforce_api_fieldmap_add_form',
),
'access arguments' => array(
'administer salesforce',
),
'type' => MENU_LOCAL_TASK,
'weight' => 10,
'file' => 'salesforce_api.admin.inc',
);
$items[SALESFORCE_PATH_FIELDMAPS . '/%/edit'] = array(
'title' => 'Edit fieldmap',
'description' => 'Edit an existing fieldmap.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'salesforce_api_fieldmap_edit_form',
4,
),
'access arguments' => array(
'administer salesforce',
),
'type' => MENU_CALLBACK,
'file' => 'salesforce_api.admin.inc',
);
$items[SALESFORCE_PATH_FIELDMAPS . '/%/clone'] = array(
'title' => 'Clone a fieldmap',
'description' => 'Clone an existing fieldmap.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'salesforce_api_fieldmap_clone_form',
4,
),
'access arguments' => array(
'administer salesforce',
),
'type' => MENU_CALLBACK,
'file' => 'salesforce_api.admin.inc',
);
$items[SALESFORCE_PATH_FIELDMAPS . '/%/delete'] = array(
'title' => 'Delete fieldmap',
'description' => 'Delete an existing fieldmap.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'salesforce_api_fieldmap_delete_form',
4,
),
'access arguments' => array(
'administer salesforce',
),
'type' => MENU_CALLBACK,
'file' => 'salesforce_api.admin.inc',
);
$items[SALESFORCE_PATH_OBJECT] = array(
'title' => 'Object setup',
'description' => 'Define which SalesForce objects you would like to be available in your Drupal site.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'salesforce_api_admin_object',
),
'access arguments' => array(
'administer salesforce',
),
'type' => MENU_LOCAL_TASK,
'file' => 'salesforce_api.admin.inc',
);
$items[SALESFORCE_PATH_OBJECT . '/%'] = array(
'title' => 'Object setup',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'salesforce_api_admin_object_settings',
count(explode('/', SALESFORCE_PATH_OBJECT)),
),
'access arguments' => array(
'administer salesforce',
),
'type' => MENU_CALLBACK,
'file' => 'salesforce_api.admin.inc',
);
return $items;
}
/**
* Implements hook_permission().
*/
function salesforce_api_permission() {
return array(
'administer salesforce' => array(
'title' => t('Administer SalesForce'),
'description' => t('Administer SalesForce'),
'restrict access' => TRUE,
),
);
}
/**
* Creates an object used for communicating with the Salesforce server and
* performs a login to verify the API credentials.
*
* @param $username
* Username for Salesforce. An email address, most likely. If none passed,
* sitewide creds will be used
* @param $password
* Password to Salesforce account.
* @param $token
* Security token from Salesforce.
* @param $reconnect
* By default, subsequent calls to this function will return the same, already
* connected Salesforce object as preceding calls. Setting this variable to
* TRUE will cause a new connection to be established instead.
* @return
* The DrupalSalesforce object used to communicate with the Salesforce server
* if successful or FALSE if a connection could not be established.
*/
function salesforce_api_connect($username = FALSE, $password = FALSE, $token = FALSE, $reconnect = FALSE) {
static $sf = FALSE;
module_load_include('inc', 'salesforce_api', 'salesforce.class');
// Return the previously connected object.
if ($sf && !$reconnect) {
return $sf;
}
// Boolean, whether we are connecting with the default website user or not.
$default_site_user = $username == variable_get('salesforce_api_username', FALSE);
// Load up the sitewide API credentials if no others were provided:
$username = $username ? $username : variable_get('salesforce_api_username', '');
$password = $password ? $password : variable_get('salesforce_api_password', '');
$token = $token ? $token : variable_get('salesforce_api_token', '');
// Fail early if we didn't receive an API username, password, or token.
if (empty($username) || empty($password) || empty($token)) {
DrupalSalesforce::watchdog(SALESFORCE_LOG_SOME, 'Connection to Salesforce
failed because API credentials have not been set.', array(), WATCHDOG_ERROR);
return FALSE;
}
// Create a new Salesforce object with the API credentials.
$sf = new DrupalSalesforce($username, $password, $token);
if (!is_object($sf)) {
DrupalSalesforce::watchdog(SALESFORCE_LOG_SOME, 'Connection to Salesforce
failed. Failed to create DrupalSalesforce wrapper.', WATCHDOG_ERROR);
return FALSE;
}
// Attempt a login.
if ($sf
->login()) {
// Mimick expired password state to debug.
// $sf->login->passwordExpired = TRUE;
if ($sf->login->passwordExpired) {
if ($default_site_user) {
salesforce_api_reset_expired_password($sf);
}
elseif (user_access('administer salesforce')) {
drupal_set_message(t('Your Salesforce account password expired. Please
<a href="https://login.salesforce.com/">login to Salesforce.com</a> and
change your password.'), 'error');
}
else {
DrupalSalesforce::watchdog(SALESFORCE_LOG_SOME, 'Connection to Salesforce
due to expired password for @user.', array(
'@user' => $username,
), WATCHDOG_ERROR);
}
}
}
else {
DrupalSalesforce::watchdog(SALESFORCE_LOG_SOME, 'Connection to Salesforce
failed with response @resp.', array(
'@resp' => $sf->client
->getLastResponse(),
), WATCHDOG_ERROR);
// Or return FALSE to indicate the failure.
$sf = FALSE;
}
return $sf;
}
/**
* Helper function for salesforce_api_connect() to reset an expired password,
* for the website's default salesforce user only.
*
* @param $sf
* The logged in DrupalSalesforce object with an expired password.
* @return
* void
*/
function salesforce_api_reset_expired_password($sf) {
// Append one letter and one digit to the password to make sure we meet
// salesforce's password validation requirements.
$new_password = user_password() . 'z9';
// setPassword() may throw InvalidIdFault or UnexpectedErrorFault exceptions.
$sf->client
->setPassword($sf->login->userId, $new_password);
variable_set('salesforce_api_password', $new_password);
// Salesforce changes the security token when the password gets changed and
// sends an email with the new security token. The new security token can
// not be retrieved via the API.
variable_del('salesforce_api_token');
// Log the event and alert admins about required steps to complete.
$vars = array(
'%user' => $sf->login->userInfo->userFullName,
'%email' => $sf->login->userInfo->userEmail,
'!uri' => url(SALESFORCE_PATH_ADMIN, array(
'absolute' => TRUE,
)),
);
DrupalSalesforce::watchdog(SALESFORCE_LOG_ALL, 'The password for the salesforce
account %user expired. Drupal changed it and saved the new password.
Salesforce updated the security token and emailed it to %email.', $vars);
DrupalSalesforce::watchdog(SALESFORCE_LOG_SOME, 'Provide the new security token
at <a href="!uri">Drupal\'s Salesforce settings page</a>. Salesforce
emailed it to %email.', $vars, WATCHDOG_ALERT);
// If salesforce connects on pages for anonymous users then these messages should not be displayed.
if (user_access('administer salesforce')) {
drupal_set_message(t('The password for the salesforce account %user expired.
Drupal changed it and saved the new password. Salesforce updated the
security token and emailed it to %email.', $vars));
drupal_set_message(t('Provide the new security token at
<a href="!uri">Drupal\'s Salesforce settings page</a>.
Salesforce emailed it to %email.', $vars), 'error');
}
}
/**
* Implements hook_fieldmap_objects().
*
* This will pull a cached version (if possible) of the available SF fields for
* the object(s) in question. Prevent excess querying!
*/
function salesforce_api_fieldmap_objects($type = 'salesforce') {
$objects = array();
// Define the data fields available for Salesforce objects.
if ($type == 'salesforce') {
$cache = cache_get('salesforce_api_sf_objects');
if (!$cache || $cache->data == '') {
$objects = salesforce_api_cache_build();
}
else {
// to mimic drupal 7's data structure -- entity->bundle->data -- add a
// redundant layer of indirection here.
$objects = $cache->data;
}
}
return array(
'salesforce' => $objects,
);
}
/**
* Recreate the salesforce object cache
*/
function salesforce_api_cache_build() {
$sf_objects = variable_get('salesforce_api_enabled_objects', array(
'Campaign',
'Contact',
'Lead',
));
$sf = salesforce_api_connect();
$result = salesforce_api_describeSObjects($sf_objects);
foreach ($sf_objects as $i => $obj) {
$objects[$obj] = salesforce_api_object_to_fieldmap_fields($result[$obj]);
}
// find the expiry time
$lifetime = variable_get('salesforce_api_object_expire', CACHE_PERMANENT);
$expire = $lifetime == CACHE_PERMANENT ? CACHE_PERMANENT : REQUEST_TIME + $lifetime;
cache_set('salesforce_api_sf_objects', $objects, $table = 'cache', $expire, $headers = NULL);
drupal_set_message(t('Salesforce object cache has been refreshed.'));
return $objects;
}
/**
* Returns an array of system fields that are retrievable from Salesforce.
*/
function salesforce_api_fieldmap_system_fields() {
$fields = array(
'Id' => array(
'label' => t('Salesforce ID'),
),
'IsDeleted' => array(
'label' => t('Is the object deleted?'),
),
'CreatedById' => array(
'label' => t('User ID of the creator'),
),
'CreatedDate' => array(
'label' => t('Creation date and time'),
),
'LastModifiedById' => array(
'label' => t('User ID of the last modifier'),
),
'LastModifiedDate' => array(
'label' => t('Last user modification date and time'),
),
'SystemModstamp' => array(
'label' => t('Last user or system modification date and time'),
),
);
return $fields;
}
/**
* Saves a fieldmap to the database.
*
* @param $map;
* An array containing the fieldmap data using the following keys and values:
* - fieldmap: the numeric index of the fieldmap.
* - drupal: the name of a Drupal object.
* - salesforce: the name of a Salesforce object.
* - automatic: whether or not the sync should be automatic
* - description: a short title or description of the fieldmap
* - fields: an array that maps source fields (as keys) to their corresponding
* target fields (as values).
*/
function salesforce_api_fieldmap_save(&$map) {
$primary_key = !empty($map['fieldmap']) ? 'fieldmap' : NULL;
if (is_array($map['fields'])) {
$map['fields'] = serialize($map['fields']);
}
drupal_write_record('salesforce_field_map', $map, $primary_key);
}
/**
* Loads a fieldmap from the database.
*
* @param $fieldmap
* The index of the fieldmap to load.
* @return
* An array containing the fieldmap data.
*/
function salesforce_api_fieldmap_load($fieldmap) {
static $maps;
if (!isset($maps[$fieldmap]) && $fieldmap != '' && is_numeric($fieldmap)) {
$map = db_select('salesforce_field_map', 's')
->fields('s')
->condition('fieldmap', $fieldmap)
->execute()
->fetch(PDO::FETCH_ASSOC);
if (isset($map['fields'])) {
$map['fields'] = unserialize($map['fields']);
}
$maps[$fieldmap] = $map;
}
return $maps[$fieldmap];
}
/**
* Remove a field from all fieldmaps. This is particularly useful for implementations
* of hook_content_fieldapi('delete instance'). May be use to delete an occurence
* in a single fieldmap (by supplying entity/bundle and/or salesforce_type), or every
* occurence in all fieldmaps (by supplying only fieldname).
*
* @param $fieldname
* The name of the field to be deleted. Either a CCK field, or a Salesforce field
* @param $entity (optional)
* If given, limit deleting of the field to this Drupal content type
* @param $bundle (optional)
* If given, limit deleting of the field to this Drupal content type
* @param $salesforce_type (optional)
* If given, limit
* @see sf_node/sf_node.module:sf_node_content_fieldapi
* @todo I'm sure this can be done more elegantly, but I can't spend anymore braincells on it right now.
*/
function salesforce_api_fieldmap_field_delete($fieldname, $entity = NULL, $bundle = NULL, $salesforce_type = NULL) {
$query = db_select('salesforce_field_map', 's');
$query
->fields('s', array(
'fieldmap',
));
if ($entity) {
$query
->condition('drupal_entity', $entity);
}
if ($bundle) {
$query
->condition('drupal_bundle', $bundle);
}
if ($salesforce_type) {
$query
->condition('salesforce', $salesforce_type);
}
$result = $query
->execute();
while ($fieldmap_id = $result
->fetchField()) {
$map = salesforce_api_fieldmap_load($fieldmap_id);
// In the extremely unlikely event that a Salesforce field and a Drupal
// field share the same name, this function handles both.
if ($entity || $bundle) {
$key1 = array_search($fieldname, $map['fields']);
if ($key1) {
unset($map['fields'][$key1]);
drupal_set_message(t('Removed Drupal field from salesforce_api !link', array(
'!link' => l('fieldmap ' . $fieldmap_id, SALESFORCE_PATH_OBJECT . '/' . $fieldmap_id),
)));
salesforce_api_fieldmap_save($map);
}
}
if ($salesforce_type) {
if (!empty($map['fields'][$key2])) {
unset($map['fields'][$key2]);
drupal_set_message(t('Removed Salesforce field from salesforce_api !link', array(
'@link' => l('fieldmap ' . $fieldmap_id, SALESFORCE_PATH_OBJECT . '/' . $fieldmap_id),
)));
salesforce_api_fieldmap_save($map);
}
}
}
}
/**
* Clones a fieldmap.
*
* @param $fieldmap
* The index of the fieldmap to clone.
* @return
* The newly created fieldmap or FALSE if the clone failed.
*/
function salesforce_api_fieldmap_clone($fieldmap) {
// Load the fieldmap from the database.
$map = salesforce_api_fieldmap_load($fieldmap);
// Return FALSE if the source fieldmap does not exist.
if (empty($map)) {
return FALSE;
}
// Save the old fieldmap id, save a new one, and return the new one.
unset($map['fieldmap']);
salesforce_api_fieldmap_save($map);
return !empty($map['fieldmap']) ? $map : FALSE;
}
/**
* Deletes a fieldmap from the database.
*
* @param $fieldmap
* The index of the fieldmap to delete.
*/
function salesforce_api_fieldmap_delete($fieldmap) {
db_delete('salesforce_field_map')
->condition('fieldmap', $fieldmap)
->execute();
db_delete('salesforce_object_map')
->condition('fieldmap', $fieldmap)
->execute();
if (function_exists('sf_prematch_match_by_delete')) {
sf_prematch_match_by_delete($fieldmap);
}
}
/**
* Returns an array of fieldmaps for use as options in the Forms API.
*
* @param $entity
* Filters the fieldmaps by entity.
* @param $bundle
* Filters the fieldmaps by bundle
* @param $salesforce
* Filters the fieldmaps by Salesforce object.
* @param $automatic
* Optional: Filter the fieldmaps to only pull those marked automatic.
* @return
* A FAPI options array of all the matching fieldmaps.
*/
function salesforce_api_fieldmap_options($entity = NULL, $bundle = NULL, $salesforce = NULL) {
$options = array();
// This does not need to not be optimized for perfomance since it's only an admin interface.
$query = db_select('salesforce_field_map', 's')
->fields('s');
if (!empty($entity)) {
$query
->condition('drupal_entity', $entity);
}
if (!empty($bundle)) {
$query
->condition('drupal_bundle', $bundle);
}
if (!empty($salesforce)) {
$query
->condition('salesforce', $salesforce, 'LIKE');
}
$result = $query
->execute();
while ($map = $result
->fetch(PDO::FETCH_ASSOC)) {
// Setup some replacement args for the label.
$args = array(
'@drupal' => salesforce_api_fieldmap_object_label('drupal', $map['drupal_bundle'], $map['drupal_entity']),
'@salesforce' => salesforce_api_fieldmap_object_label('salesforce', 'salesforce', $map['salesforce']),
);
$options[$map['fieldmap']] = t('Drupal @drupal to Salesforce @salesforce', $args);
}
return $options;
}
/**
* Returns all or a subset of the objects defined via hook_sf_fieldmap().
*
* @param (string) $type
* valid values: 'drupal' or 'salesforce'
* Specify a type to filter the return value to objects of that type.
* @param (string) $entity
* valid values: if $type == 'salesforce', this should also be 'salesforce'
* if $type == 'drupal', this should be a valid entity name
* Specify an entity name to filter the return value to that entity alone.
* If this parameter is supplied, you must specify a type.
* @param (string) $bundle
* Specify a bundle name to further filter the return value by bundle.
* If this parameter is supplied, you must specify an entity.
* @return
* Return value structure depends on the arguments provided.
* If no arguments, all fieldmap objects will be returned.
* If $type is specified, only objects of that type will be returned, etc.
*/
function salesforce_api_fieldmap_objects_load($type = NULL, $entity = NULL, $bundle = NULL) {
static $objects = array();
// If we have not yet cached the object definitions...
if (empty($objects)) {
// Find all the Drupal objects defined by hook_sf_fieldmap().
$objects['drupal'] = module_invoke_all('fieldmap_objects', 'drupal');
// Get all the Salesforce objects defined by hook_sf_fieldmap().
$objects['salesforce'] = module_invoke_all('fieldmap_objects', 'salesforce');
// Allow other modules to modify the object definitions.
foreach (module_implements('fieldmap_objects_alter') as $module) {
$function = $module . '_fieldmap_objects_alter';
$function($objects);
}
}
// If a particular object type was specified...
if (!empty($type)) {
// And a particular object was specified...
if (!empty($entity)) {
// Return that object definition if it exists or FALSE if it does not.
if (!empty($bundle)) {
if (isset($objects[$type][$entity][$bundle])) {
return $objects[$type][$entity][$bundle];
}
else {
return FALSE;
}
}
else {
if (isset($objects[$type][$entity])) {
return $objects[$type][$entity];
}
else {
return FALSE;
}
}
}
else {
if (isset($objects[$type])) {
return $objects[$type];
}
else {
return FALSE;
}
}
}
return $objects;
}
/**
* Returns the label for the object of the specified type and name.
* @see salesforce_api_fieldmap_objects_load()
*/
function salesforce_api_fieldmap_object_label($type, $entity, $bundle) {
// Get the object definition.
$object = salesforce_api_fieldmap_objects_load($type, $entity, $bundle);
// If no label is specified, return the object name.
if (empty($object['label'])) {
return check_plain($entity . ': ' . $bundle);
}
return $object['label'];
}
/**
* Returns a string of description text for the specified fieldmap.
*/
function salesforce_api_fieldmap_description($map) {
return t('Fieldmap @index maps Salesforce %salesforce objects to Drupal %drupal objects.', array(
'@index' => $map['fieldmap'],
'%drupal' => salesforce_api_fieldmap_object_label('drupal', $map['drupal_entity'], $map['drupal_bundle']),
'%salesforce' => salesforce_api_fieldmap_object_label('salesforce', 'salesforce', $map['salesforce']),
));
}
/**
* Returns a FAPI options array for specifying a field from the source object to
* associate with the target field.
*
* @param $object
* The source object whose fields we need to filter into the options array.
* @param $type
* The type of the target field's object.
* @param $name
* The name of the target object.
* @param $field
* The name of the target field.
* @return
* A FAPI options array of all the available fields that can map to the
* target field.
*/
function salesforce_api_fieldmap_field_options($object, $type = NULL, $name = NULL, $field = NULL) {
// Define the options array with a blank value.
$options = array(
'' => '',
);
// TODO: Consider filtering these based on the object definition. For now
// this function simply uses any field defined for the source object.
// Loop through all the fields of the source object.
foreach ($object['fields'] as $key => $data) {
// Add the field to the options array in the right options group.
if (!empty($data['group'])) {
$options[$data['group']][$key] = $data['label'];
}
else {
$options[t('Core fields')][$key] = $data['label'];
}
}
return $options;
}
/**
* Creates an object for export to Salesforce based on the supplied Drupal
* object and fieldmap.
*
* @param $fieldmap
* The index of the fieldmap used to filter the Drupal object into the export.
* @param $drupal_data
* The Drupal object used to generate the export.
* @return
* An object containing data ready for export to Salesforce or FALSE if
* the operation failed.
*/
function salesforce_api_fieldmap_export_create($fieldmap, $drupal_data = NULL) {
// Load the fieldmap from the database.
$map = salesforce_api_fieldmap_load($fieldmap);
// Fail if the fieldmap does not exist.
if (!$map) {
return FALSE;
}
$drupal_object_definition = salesforce_api_fieldmap_objects_load('drupal', $map['drupal_entity'], $map['drupal_bundle']);
$sf_object_definition = salesforce_api_fieldmap_objects_load('salesforce', 'salesforce', $map['salesforce']);
$object = new stdClass();
// Loop through the fields on the fieldmap.
foreach ($map['fields'] as $sf_fieldname => $drupal_fieldname) {
$sf_field_definition = $sf_object_definition['fields'][$sf_fieldname];
$updateable = $sf_field_definition['type'] & SALESFORCE_FIELD_UPDATEABLE;
$createable = $sf_field_definition['type'] & SALESFORCE_FIELD_CREATEABLE;
// Don't try to update or create fields to which those actions do not apply.
if (!$updateable && !$createable || empty($drupal_data->salesforce['sfid']) && !$createable || !empty($drupal_data->salesforce['sfid']) && !$updateable) {
continue;
}
// If a handler is specified for retrieving a value for the Drupal field...
if (isset($drupal_object_definition['fields'][$drupal_fieldname]['export'])) {
$drupal_field_export_handler = $drupal_object_definition['fields'][$drupal_fieldname]['export'];
$drupal_field_definition = $drupal_object_definition['fields'][$drupal_fieldname];
// Get the value for the field from the handler function.
$object->{$sf_fieldname} = htmlentities($drupal_field_export_handler($drupal_data, $drupal_fieldname, $drupal_field_definition, $sf_field_definition));
}
elseif (isset($drupal_data->{$drupal_fieldname})) {
$object->{$sf_fieldname} = htmlentities($drupal_data->{$drupal_fieldname});
}
}
// Before we return the object, we need to check for any fieldsToNull.
// Leaving a field blank will not erase an existing value from Salesforce.
// Any such value must be explicitly set to NULL.
// TODO: Fields should be checked for nillable before getting added to fieldsToNull
$props = get_object_vars($object);
foreach ($props as $key => $value) {
$sf_field_definition = $sf_object_definition['fields'][$sf_fieldname];
// Salesforce treats the following values differently than NULL.
if ($value === FALSE || $value === 0 || $value === '0' || $value === 'FALSE') {
$object->{$key} = $value = 0;
}
elseif (empty($value)) {
$nillable = $sf_field_definition['type'] & SALESFORCE_FIELD_NILLABLE;
// If the field is not nillable, don't try to set it to NULL
if (!$nillable) {
switch ($sf_field_definition['salesforce']['type']) {
case 'boolean':
$object->{$key} = 0;
break;
case 'string':
$object->{$key} = t('(blank)');
break;
default:
unset($object->{$key});
break;
}
}
else {
$object->fieldsToNull = $key;
// Enterprise client can only NULL one field per transaction.
// This is a bug beyond our control. For now, we just have to deal with it.
// The following doesn't actually work:
// if (!empty($object->fieldsToNull)) {
// $object->fieldsToNull .= '; ';
// }
}
}
}
return $object;
}
/**
* Loads the mapping data given identifying information.
*
* @param string $entity
* The entity type of the Drupal object you are requesting data for;
* e.g. node or user.
* @param string $bundle
* The bundle the Drupal object belongs to
* @param mixed $id_or_ids
* The associated unique ID or IDs used to identify the object in Drupal.
* @return
* The fetched query result.
*/
function salesforce_api_id_load($entity, $bundle, $id_or_ids, $key = 'fieldmap') {
// Query the main ID table for the associated data.
$op = is_array($id_or_ids) ? 'IN' : '=';
$query = db_select('salesforce_object_map', 's')
->fields('s', array(
'fieldmap',
'sfid',
'oid',
))
->condition('drupal_entity', $entity)
->condition('oid', $id_or_ids, $op);
if ($bundle) {
$query
->condition('drupal_bundle', $bundle);
}
return $query
->execute()
->fetchAllAssoc($key, PDO::FETCH_ASSOC);
}
/**
* Get an object id using the salesforce id
*
* @param $sfid
* A saleforce id
* @param $type
* The type of the Drupal object you are requesting data for; node or user.
* @return
* The associated unique ID used to identify the object in Drupal or FALSE.
*/
function salesforce_api_get_id_with_sfid($sfid, $entity, $bundle, $key = 'sfid') {
return db_select('salesforce_object_map', 's')
->fields('s', array(
'oid',
))
->condition('sfid', $sfid)
->condition('drupal_entity', $entity)
->condition('drupal_bundle', $bundle)
->execute()
->fetchAllAssoc($key);
}
/**
* Saves the Salesforce ID and fieldmap index of a Drupal object.
*
* @param $entity
* The entity you are saving; e.g. node or user.
* @param $bundle
* The bundle you are saving for; e.g. node type or vocab name
* @param $oid
* The associated unique ID used to identify the object in Drupal.
* @param $sfid
* The Salesforce ID of the associated object in the Salesforce database.
* @param $fieldmap
* The index of the fieldmap used to generate the export.
*/
function salesforce_api_id_save($entity, $bundle, $oid, $sfid, $fieldmap) {
db_delete('salesforce_object_map')
->condition('drupal_entity', $entity)
->condition('drupal_bundle', $bundle)
->condition('oid', $oid)
->execute();
db_insert('salesforce_object_map')
->fields(array(
'drupal_entity' => $entity,
'drupal_bundle' => $bundle,
'oid' => $oid,
'sfid' => $sfid,
'fieldmap' => $fieldmap,
))
->execute();
}
function salesforce_api_search_for_duplicates($direction, $object, $fieldmap_id) {
// Call hook_sf_find_match to give opportunity to try to match existing sf object instead
// of creating a new one. No hook_sf_find_match is defined out of the box. Developers must
// implement their own logic for this one.
return module_invoke_all('sf_find_match', $direction, $object, $fieldmap_id);
}
/**
* Implements hook_theme().
*
* Registers theme callback for admin screen
*/
function salesforce_api_theme($existing, $type, $theme, $path) {
return array(
'salesforce_api_fieldmap_edit_form_table' => array(
'render element' => 'form',
),
'salesforce_api_object_options' => array(
'render element' => 'element',
),
);
}
/**
* Wrapper for SOAP SforceBaseClient::describeGlobal
* @return an SFQueryResult object (look at ->types for an array of SF object types)
*/
function salesforce_api_describeGlobal() {
static $response;
if (!empty($response)) {
return $response;
}
$sf = salesforce_api_connect();
if ($sf === FALSE) {
$link = l('Please verify that you have completed your SalesForce credentials', SALESFORCE_PATH_ADMIN);
drupal_set_message(t('Unable to connect to SalesForce. !link', array(
'!link' => $link,
)), 'error');
return;
}
$response = $sf->client
->describeGlobal();
if (isset($response->sobjects)) {
$response->types = $response->sobjects;
unset($response->sobjects);
}
return $response;
}
/**
* Convert Salesforce object fields to fieldmap array for saving
*/
function salesforce_api_object_to_fieldmap_fields($object) {
$fieldmap_object = array(
'label' => t($object->label),
'fields' => array(),
);
foreach ($object->fields as $field) {
$properties = array(
'name',
'label',
'type',
'length',
'soapType',
);
$booleans = array(
'createable',
'defaultedOnCreate',
'deprecatedAndHidden',
'idLookup',
'nillable',
'restrictedPicklist',
'unique',
'updateable',
);
$source = get_object_vars($field);
$sf_definition = array_intersect_key($source, array_flip($properties));
$sf_definition['sf_type'] = 0;
foreach ($booleans as $bool) {
$sf_definition['sf_type'] |= (int) $source[$bool] * constant('SALESFORCE_FIELD_' . strtoupper($bool));
}
$fieldmap_object['fields'][$field->name] = array(
'name' => $sf_definition['name'],
'label' => $sf_definition['label'],
'type' => $sf_definition['sf_type'],
'salesforce' => $sf_definition,
);
}
return $fieldmap_object;
}
/**
* Implements hook_cron().
*/
function salesforce_api_cron() {
$cache = cache_get('salesforce_api_sf_objects');
// if the cache has already been delete or is expired then rebuild
if (!$cache || REQUEST_TIME > $cache->expire) {
salesforce_api_cache_build();
}
return;
}
/**
* Wrapper for SOAP SforceBaseClient::describeSObject
* Given an sf object type, return the SF Object definition
* @param string type : the machine-readable name of the SF object type.
*/
function salesforce_api_describeSObject($type) {
if (!is_string($type)) {
drupal_set_message(t('DescribeSObject expects a string. @type recieved.', array(
'@type' => gettype($type),
)), 'error');
return FALSE;
}
$objects = salesforce_api_describeSObjects($type);
if (!empty($objects[$type])) {
return $objects[$type];
}
else {
drupal_set_message(t('DescribeSObject failed to find @type.', array(
'@type' => $type,
)), 'error');
return FALSE;
}
}
/**
* Wrapper for SOAP SforceBaseClient::describeSObjects
* Given an array of sf object type, return an associative, normalized array of
* SF object definitions, indexed on machine-readable names of SObjects
* @param array types : an array of machine-readable names to SObjects
*/
function salesforce_api_describeSObjects($types) {
static $objects;
if (is_string($types)) {
$types = array(
$types,
);
}
if (!is_array($types)) {
drupal_set_message(t('DescribeSObjects expects an array. @types recieved.', array(
'@types' => gettype($types),
)), 'error');
return FALSE;
}
// There is no reason to describe the same object twice in one HTTP request.
// Use a static cache to save API calls and bandwidth.
if (!empty($objects)) {
$outstanding = array_diff($types, array_keys($objects));
if (empty($outstanding)) {
$ret = array();
foreach ($types as $k) {
$ret[$k] = $objects[$k];
}
return $ret;
}
}
if (is_string($types)) {
$types = array(
$types,
);
}
try {
$sf = salesforce_api_connect();
if ($sf === FALSE) {
$link = l('Please verify that you have completed your SalesForce credentials', SALESFORCE_PATH_ADMIN);
drupal_set_message(t('Unable to connect to SalesForce. !link', array(
'!link' => $link,
)), 'error');
return;
}
$objects = $sf->client
->describeSObjects(array_values($types));
} catch (Exception $e) {
DrupalSalesforce::watchdog(SALESFORCE_LOG_SOME, 'Unable to establish Salesforce connection while issuing describeSObjects API call.', array(), WATCHDOG_ERROR);
}
if (empty($objects)) {
return array();
}
// This is the normalization part: If only one object was described, SalesForce
// returned an object instead of an array. ALWAYS return an array of objects.
if (is_object($objects)) {
$objects = array(
$objects,
);
}
// And make it an associative array for good measure.
$tmp = array();
foreach ($objects as $o) {
$tmp[$o->name] = $o;
}
$objects = $tmp;
return $objects;
}
Functions
Name | Description |
---|---|
salesforce_api_cache_build | Recreate the salesforce object cache |
salesforce_api_connect | Creates an object used for communicating with the Salesforce server and performs a login to verify the API credentials. |
salesforce_api_cron | Implements hook_cron(). |
salesforce_api_describeGlobal | Wrapper for SOAP SforceBaseClient::describeGlobal |
salesforce_api_describeSObject | Wrapper for SOAP SforceBaseClient::describeSObject Given an sf object type, return the SF Object definition |
salesforce_api_describeSObjects | Wrapper for SOAP SforceBaseClient::describeSObjects Given an array of sf object type, return an associative, normalized array of SF object definitions, indexed on machine-readable names of SObjects |
salesforce_api_fieldmap_clone | Clones a fieldmap. |
salesforce_api_fieldmap_delete | Deletes a fieldmap from the database. |
salesforce_api_fieldmap_description | Returns a string of description text for the specified fieldmap. |
salesforce_api_fieldmap_export_create | Creates an object for export to Salesforce based on the supplied Drupal object and fieldmap. |
salesforce_api_fieldmap_field_delete | Remove a field from all fieldmaps. This is particularly useful for implementations of hook_content_fieldapi('delete instance'). May be use to delete an occurence in a single fieldmap (by supplying entity/bundle and/or salesforce_type), or… |
salesforce_api_fieldmap_field_options | Returns a FAPI options array for specifying a field from the source object to associate with the target field. |
salesforce_api_fieldmap_load | Loads a fieldmap from the database. |
salesforce_api_fieldmap_objects | Implements hook_fieldmap_objects(). |
salesforce_api_fieldmap_objects_load | Returns all or a subset of the objects defined via hook_sf_fieldmap(). |
salesforce_api_fieldmap_object_label | Returns the label for the object of the specified type and name. |
salesforce_api_fieldmap_options | Returns an array of fieldmaps for use as options in the Forms API. |
salesforce_api_fieldmap_save | Saves a fieldmap to the database. |
salesforce_api_fieldmap_system_fields | Returns an array of system fields that are retrievable from Salesforce. |
salesforce_api_get_id_with_sfid | Get an object id using the salesforce id |
salesforce_api_id_load | Loads the mapping data given identifying information. |
salesforce_api_id_save | Saves the Salesforce ID and fieldmap index of a Drupal object. |
salesforce_api_menu | Implements hook_menu(). |
salesforce_api_object_to_fieldmap_fields | Convert Salesforce object fields to fieldmap array for saving |
salesforce_api_permission | Implements hook_permission(). |
salesforce_api_reset_expired_password | Helper function for salesforce_api_connect() to reset an expired password, for the website's default salesforce user only. |
salesforce_api_search_for_duplicates | |
salesforce_api_theme | Implements hook_theme(). |