You are here

civicrm_entity.module in CiviCRM Entity 7.2

Same filename and directory in other branches
  1. 8.3 civicrm_entity.module
  2. 7 civicrm_entity.module

File

civicrm_entity.module
View source
<?php

/**
 * @file
 * Implement CiviCRM entities as a Drupal Entity.
 */
module_load_include('inc', 'civicrm_entity', 'civicrm_entity.ds');
module_load_include('inc', 'civicrm_entity', 'civicrm_entity.default_form');

/**
 * Implements hook_menu().
 *
 * @return array
 */
function civicrm_entity_menu() {
  $items = array();
  $items['admin/structure/civicrm-entity'] = array(
    'title' => 'CiviCRM Entity',
    'description' => 'Available CiviCRM Entities',
    'type' => MENU_NORMAL_ITEM,
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer CiviCRM Entity',
    ),
    'page callback' => 'system_admin_menu_block_page',
    'file path' => drupal_get_path('module', 'system'),
    'file' => 'system.admin.inc',
  );
  $items['admin/structure/civicrm-entity/settings'] = array(
    'title' => 'Configure CiviCRM Entity',
    'description' => 'Configure CiviCRM settings, such as enabled entities',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'civicrm_entity_admin_settings_form',
    ),
    'access arguments' => array(
      'administer CiviCRM Entity',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'civicrm_entity.admin_form.inc',
  );
  $items['civicrm-entity/autocomplete/%'] = array(
    'title' => 'CiviCRM entity autocomplete',
    'page callback' => 'civicrm_entity_autocomplete',
    'page arguments' => array(
      2,
    ),
    'access callback' => 'civicrm_entity_op_access',
    'access arguments' => array(
      'edit',
      2,
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_menu_alter().
 *
 * Entity API creates all the fields page links but the basic 'manage' page is missing
 * so we use the /fields page at that url
 */
function civicrm_entity_menu_alter(&$items) {
  foreach (entity_get_info() as $entity_name => $entity_info) {
    if (!empty($entity_info['module']) && $entity_info['module'] == 'civicrm_entity') {
      foreach ($entity_info['bundles'] as $file_type => $bundle_info) {
        if (isset($bundle_info['admin'])) {

          // Get the base path and access.
          $path = $bundle_info['admin']['path'];
          if (isset($items[$path . '/fields'])) {
            $items[$path] = $items[$path . '/fields'];
            $items[$path]['type'] = MENU_NORMAL_ITEM;
            $items[$path]['title'] = $entity_info['label'];
            $items[$path]['description'] = t('CiviCRM @entity entity', array(
              '@entity' => $entity_name,
            ));
            $items[$path . '/fields']['type'] = MENU_DEFAULT_LOCAL_TASK;
          }
        }
      }
    }
  }
}

/**
 * Menu callback for autocomplete search function
 *
 * @param $entity_type
 */
function civicrm_entity_autocomplete($entity_type, $string = '') {
  $matches = array();
  if ($string) {
    switch ($entity_type) {
      case 'Contact':
        $matches = _civicrm_entity_db_select($entity_type, $string, 'display_name');
        break;
      case 'Event':
        $matches = _civicrm_entity_db_select($entity_type, $string, 'title');
        break;
    }
  }
  drupal_json_output($matches);
}

/**
 * Utility function to do db_select for FK reference autocomplete widgets
 *
 * @param $entity_type
 * @param $string
 * @param $condition_column
 * @return array
 */
function _civicrm_entity_db_select($entity_type, $string, $condition_column) {
  $matches = array();
  $result = db_select('civicrm_' . strtolower($entity_type))
    ->fields('civicrm_' . strtolower($entity_type), array(
    'id',
    $condition_column,
  ))
    ->condition($condition_column, db_like($string) . '%', 'LIKE')
    ->range(0, 10)
    ->execute();
  foreach ($result as $record) {
    $matches[$record->id] = check_plain($record->{$condition_column});
  }
  return $matches;
}

/**
 * Implements hook_permission().
 */
function civicrm_entity_permission() {
  return array(
    'civicrm_entity.rules.administer' => array(
      'title' => t('Administer CiviCRM rule configurations'),
      'restrict access' => TRUE,
    ),
    'administer CiviCRM Entity' => array(
      'title' => t('Administer CiviCRM Entity'),
      'restrict access' => TRUE,
    ),
  );
}

/**
 * Implements hook_theme().
 *
 * Lets Drupal know to look for templates for the entity types
 *
 * @return array $themes
 */
function civicrm_entity_theme() {
  $civicrm_entity_info = civicrm_entity_get_supported_entity_info();
  $enabled_entities = _civicrm_entity_enabled_entities();
  $themes = array();
  foreach ($civicrm_entity_info as $drupal_entity => $data) {
    if (!empty($enabled_entities[$drupal_entity])) {
      $dashkey = str_replace('_', '-', $drupal_entity);
      if (isset($data['theme'])) {
        $themes[$drupal_entity] = [
          'render element' => 'elements',
        ];
        if (isset($data['theme']['template'])) {
          $themes[$drupal_entity]['template'] = $data['theme']['template'];
        }
        if (isset($data['theme']['path'])) {
          $themes[$drupal_entity]['path'] = $data['theme']['path'];
        }
      }
    }
  }
  return $themes;
}

/**
 * CiviCRM Entity view,edit,delete form autoloader callback
 *
 * @param $entity_type
 * @param $id
 * @return mixed
 */
function civicrm_entity_loader_load($id, $entity_type) {
  $entity = entity_load_single($entity_type, $id);
  if ($entity) {
    return $entity;
  }
  else {
    return $id;
  }
}

/**
 * Entity access callback.
 *
 * @param $op
 * @param $entity
 * @param $account
 * @param $entity_type
 *
 * @return bool
 */
function civicrm_entity_access($op, $entity, $account, $entity_type) {
  if (is_null($account)) {
    global $user;
    $account = $user;
  }

  // now handles update as a separate op to permission
  return user_access('administer CiviCRM') || civicrm_entity_op_access($op, $entity_type, $entity, $account);
}

/**
 * Granular per-entity CRUD access control
 *
 * @param $op
 * @param $entity_type
 * @param null $entity
 * @param null $account
 * @return TRUE|FALSE
 */
function civicrm_entity_op_access($op, $entity_type, $entity = NULL, $account = NULL) {
  if (is_null($account)) {
    global $user;
    $account = $user;
  }
  $civicrm_entity_info = civicrm_entity_get_supported_entity_info();
  $enabled_entities = _civicrm_entity_enabled_entities();
  if (empty($enabled_entities[$entity_type])) {
    return FALSE;
  }
  if (isset($civicrm_entity_info[$entity_type]['permissions'][$op])) {
    $permissions = $civicrm_entity_info[$entity_type]['permissions'][$op];
    if (!empty($civicrm_entity_info[$entity_type]['permissions']['access callback'])) {
      $access_callback = $civicrm_entity_info[$entity_type]['permissions']['access callback'];
      return call_user_func($access_callback, $op, $entity_type, $entity, $account, $permissions);
    }
    elseif (!empty($permissions)) {
      if (is_array($permissions)) {
        if (count($permissions)) {
          foreach ($permissions as $permission) {
            if (!user_access($permission, $account)) {
              return FALSE;
            }
          }
          return TRUE;
        }
        else {
          return user_access('administer CiviCRM', $account);
        }
      }
      else {
        return user_access($permissions, $account);
      }
    }
  }
  return user_access('administer CiviCRM', $account);
}

/**
 * Implements hook_views_data_alter().
 */
function civicrm_entity_views_data_alter(&$data) {
  $civicrm_entity_types = civicrm_entity_get_supported_entity_info();
  $enabled_entities = _civicrm_entity_enabled_entities();
  foreach ($civicrm_entity_types as $civicrm_entity_type => $info) {
    if (!empty($enabled_entities[$civicrm_entity_type]) && !empty($data[$civicrm_entity_type])) {
      foreach ($data[$civicrm_entity_type] as $property => $prop_info) {

        // unset auto-generated custom field views handlers due to civicrm_entity entity metadata coming from API getfields call
        if (strpos($property, 'custom_') === 0) {
          $name_array = explode('_', $property);
          if (isset($name_array[1]) && is_numeric($name_array[1])) {
            unset($data[$civicrm_entity_type][$property]);
          }
        }
      }
    }
  }
  if (!empty($enabled_entities['civicrm_contribution_recur']) && !empty($data['civicrm_contribution_recur']['next_sched_contribution'])) {
    foreach ($data['civicrm_contribution_recur'] as $property => $property_data) {
      if (strpos($property, 'next_sched_contribution') === 0) {
        $property_name_components = explode('_', $property);
        if (!empty($property_name_components[3])) {
          $new_name = $property_name_components[0] . '_' . $property_name_components[1] . '_' . $property_name_components[2] . '_date_' . $property_name_components[3] . (!empty($property_name_components[4]) ? '_' . $property_name_components[4] : '');
        }
        else {
          $new_name = implode('_', $property_name_components) . '_date';
        }
        $data['civicrm_contribution_recur'][$new_name] = $data['civicrm_contribution_recur'][$property];
        unset($data['civicrm_contribution_recur'][$property]);
      }
    }
  }
}

/**
 * Implements hook_schema_alter().
 *
 * Note we are just doing this in a very simple form relationship type
 * which is not defined by views at this stage. We have the problem
 * that the CiviCRM views integration uses the _data hook & would need
 * to use _data_alter hook to be compatible with entity views
 * integration
 *
 * @param $schema
 */
function civicrm_entity_schema_alter(&$schema) {
  $schema_entities = _civicrm_entity_enabled_entities();
  foreach ($schema_entities as $drupal_entity => $civicrm_entity) {
    $schema[$drupal_entity] = civicrm_entity_get_schema($drupal_entity);
  }
}

/**
 * Get schema for entities.
 *
 * This approach may not be required as using the schema_alter hook
 * (as opposed to schema_hook) seems to get around a bunch of the
 * reasons I used a separate schema.
 *
 * @param $table
 *
 * @return array
 */
function civicrm_entity_get_schema($table) {
  if (!civicrm_initialize(TRUE)) {
    return;
  }
  $schema = array();
  $schema[$table] = array(
    'description' => 'The base table for ' . $table,
    'primary key' => array(
      'id',
    ),
    'fields' => array(),
  );
  $civicrm_entity = substr($table, 8);
  $fields = civicrm_api($civicrm_entity, 'getfields', array(
    'version' => 3,
  ));
  $fields = $fields['values'];
  foreach ($fields as $fieldname => $field_spec) {
    if (empty($field_spec['name'])) {
      continue;
    }
    $unique_name = empty($field_spec['uniqueName']) ? $fieldname : $field_spec['uniqueName'];
    $schema[$table]['fields'][$unique_name] = array(
      'real_field' => $field_spec['name'],
      'description' => _civicrm_entity_get_title($field_spec),
      'unsigned' => TRUE,
      'not null' => TRUE,
    ) + civicrm_entity_get_field_type($field_spec);
  }
  return empty($schema[$table]) ? array() : $schema[$table];
}

/**
 * Please document this function.
 *
 * @param $field_spec
 *
 * @return array
 */
function civicrm_entity_get_field_type($field_spec) {
  if ($field_spec['name'] == 'id') {
    return array(
      'type' => 'serial',
    );
  }
  if (!isset($field_spec['type'])) {
    return array(
      'type' => 'text',
      'field_type' => 'text',
    );
  }
  switch ($field_spec['type']) {
    case CRM_Utils_Type::T_INT:
    case CRM_Utils_Type::T_BOOLEAN:
      return array(
        'type' => 'integer',
        'field_type' => 'number_integer',
      );
    case CRM_Utils_Type::T_MONEY:
    case CRM_Utils_Type::T_FLOAT:
      return array(
        'type' => 'float',
      );
    case CRM_Utils_Type::T_TEXT:
    case CRM_Utils_Type::T_STRING:
    case CRM_Utils_Type::T_LONGTEXT:
    case CRM_Utils_Type::T_CCNUM:
    case CRM_Utils_Type::T_EMAIL:
    case CRM_Utils_Type::T_URL:
      return array(
        'type' => 'text',
        'field_type' => 'text',
      );
    case CRM_Utils_Type::T_DATE:
    case CRM_Utils_Type::T_TIME:
    case CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME:
      return array(
        'type' => 'varchar',
        'mysql_type' => 'datetime',
      );
    case CRM_Utils_Type::T_ENUM:
      return array(
        'type' => 'varchar',
        'mysql_type' => 'enum',
      );
    case CRM_Utils_Type::T_BLOB:
    case CRM_Utils_Type::T_MEDIUMBLOB:
      return array(
        'type' => 'blob',
      );
    case CRM_Utils_Type::T_TIMESTAMP:
      return array(
        'type' => 'varchar',
        'mysql_type' => 'timestamp',
      );
  }
  return array(
    'type' => $field_spec['type'],
  );
}

/**
 * Here we declare selected CiviCRM entities to Drupal.
 *
 * This is necessary for entity module to pick them up.
 */
function civicrm_entity_entity_info() {
  $entities = _civicrm_entity_enabled_entities();
  foreach ($entities as $drupal_entity => $civicrm_entity) {
    $info[$drupal_entity] = array(
      'description' => $civicrm_entity,
      'optional' => TRUE,
      'label' => "CiviCRM " . ucwords(str_replace('_', ' ', $civicrm_entity)),
      'module' => 'civicrm_entity',
      'controller class' => 'CivicrmEntityController',
      'metadata controller class' => 'CivicrmEntityMetadataController',
      'views controller class' => 'CiviCRMEntityDefaultViewsController',
      'ui class' => 'RulesDataUIEntity',
      'fieldable' => TRUE,
      'extra fields controller class' => 'EntityDefaultExtraFieldsController',
      'access callback' => 'civicrm_entity_access',
      'admin ui' => array(
        'path' => $drupal_entity,
        'controller class' => 'CivicrmEntityUIController',
        'file' => 'civicrm_entity_controller.inc',
      ),
      'bundles' => array(
        $drupal_entity => array(
          'label' => t('CiviCRM @entity', array(
            '@entity' => ucwords($civicrm_entity),
          )),
          'admin' => array(
            'path' => 'admin/structure/civicrm-entity/' . $drupal_entity,
            'access arguments' => array(
              'administer CiviCRM Entity',
            ),
          ),
          'uri callback' => 'civicrm_entity_uri_callback',
        ),
      ),
      'view modes' => array(
        'full' => array(
          'label' => t('Full'),
          'custom settings' => TRUE,
        ),
      ),
      'entity keys' => array(
        'id' => 'id',
        'label' => _civicrm_entity_labels($drupal_entity),
      ),
      'base table' => $drupal_entity,
    );
    $label_callback = 'civicrm_entity_' . $drupal_entity . '_label_callback';
    if (function_exists($label_callback)) {
      $info[$drupal_entity]['label callback'] = $label_callback;
    }
  }
  return $info;
}

/**
 * URI Callback for entities
 *
 * @param $entity
 * @return array
 */
function civicrm_entity_uri_callback($entity) {
  $uri['path'] = str_replace('_', '-', $entity
    ->entityType()) . '/' . $entity->id;
  return $uri;
}

/**
 * Get supported CiviCRM entity information
 *
 * implement hook_civicrm_entity_supported_info() to get you custom CiviCRM API entity supported
 *
 * @param null $entity_type
 * @return array|void
 */
function civicrm_entity_get_supported_entity_info($entity_type = NULL) {
  $civicrm_entity_info = civicrm_entity_supported_entities_info();
  if (civicrm_initialize()) {
    $codeVersion = explode('.', CRM_Utils_System::version());

    // if db.ver < code.ver, time to upgrade
    if (version_compare($codeVersion[0] . '.' . $codeVersion[1], 5.28) >= 0) {
      CRM_Utils_Hook::singleton()
        ->invoke([
        'info',
      ], $civicrm_entity_info, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, 'civicrm_entity_supported_info');
    }
    elseif (version_compare($codeVersion[0] . '.' . $codeVersion[1], 4.5) >= 0) {
      CRM_Utils_Hook::singleton()
        ->invoke(1, $civicrm_entity_info, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, 'civicrm_entity_supported_info');
    }
    else {
      CRM_Utils_Hook::singleton()
        ->invoke(1, $civicrm_entity_info, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, 'civicrm_entity_supported_info');
    }
  }
  if (empty($entity_type)) {
    return $civicrm_entity_info;
  }
  elseif (isset($civicrm_entity_info[$entity_type])) {
    return $civicrm_entity_info[$entity_type];
  }
}

/**
 * Whitelist of total CiviCRM Entity related info metadata
 *
 * @return array
 */
function civicrm_entity_supported_entities_info() {
  $civicrm_entity_info = array();
  $civicrm_entity_info['civicrm_activity'] = array(
    'civicrm entity name' => 'activity',
    'label property' => 'subject',
    'permissions' => array(
      'view' => array(
        'view all activities',
      ),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(
        'delete activities',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-activity',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'source_contact_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'assignee_contact_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'target_contact_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'relationship_id',
          'target' => 'civicrm_relationship',
        ),
        array(
          'link_field' => 'parent_id',
          'target' => 'civicrm_activity',
        ),
        array(
          'link_field' => 'original_id',
          'target' => 'civicrm_activity',
        ),
      ),
      'option fields' => array(
        'activity_type_id',
        'status_id',
        'medium_id',
        'priority_id',
        'engagement_level',
      ),
      'boolean fields' => array(
        'is_auto',
        'is_current_revision',
        'is_test',
        'is_deleted',
      ),
      'date fields' => array(
        'activity_date_time',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_action_schedule'] = array(
    'civicrm entity name' => 'action_schedule',
    'label property' => 'name',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'display suite' => array(
      'date fields' => array(
        'absolute_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_address'] = array(
    'civicrm entity name' => 'address',
    'label property' => 'name',
    'permissions' => array(
      'view' => array(
        'view all contacts',
      ),
      'edit' => array(
        'edit all contacts',
      ),
      'update' => array(
        'edit all contacts',
      ),
      'create' => array(
        'edit all contacts',
      ),
      'delete' => array(
        'delete contacts',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-address',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contact_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'contact_id_contact',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'master_id',
          'target' => 'civicrm_contact',
        ),
      ),
      'option fields' => array(
        'location_type_id',
        'county_id',
        'state_province_id',
        'country_id',
      ),
      'boolean fields' => array(
        'is_primary',
        'is_billing',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_campaign'] = array(
    'civicrm entity name' => 'campaign',
    'label property' => 'title',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-campaign',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'date fields' => array(
        'start_date',
        'end_date',
        'created_date',
        'last_modified_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_case'] = array(
    'civicrm entity name' => 'case',
    'label property' => 'subject',
    'permissions' => array(
      'view' => array(
        'access all cases and activities',
      ),
      'edit' => array(
        'access all cases and activities',
      ),
      'update' => array(
        'access all cases and activities',
      ),
      'create' => array(
        'add cases',
        'access all cases and activities',
      ),
      'delete' => array(
        'delete in CiviCase',
        'access all cases and activities',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-case',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'option fields' => array(
        'case_type_id',
        'status_id',
      ),
      'boolean fields' => array(
        'is_deleted',
      ),
      'date fields' => array(
        'start_date',
        'end_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_contact'] = array(
    'civicrm entity name' => 'contact',
    'label property' => 'display_name',
    'permissions' => array(
      'view' => array(
        'view all contacts',
      ),
      'edit' => array(
        'edit all contacts',
      ),
      'update' => array(
        'edit all contacts',
      ),
      'create' => array(
        'edit all contacts',
      ),
      'delete' => array(
        'delete contacts',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-contact',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'employer_id_contact',
          'target' => 'civicrm_contact',
        ),
      ),
      'option fields' => array(
        'preferred_communication_method',
        'prefix_id',
        'suffix_id',
        'communication_style_id',
        'gender_id',
        'country_id',
        'state_province_id',
      ),
      'boolean fields' => array(
        'is_deceased',
        'do_not_email',
        'do_not_phone',
        'do_not_sms',
        'do_not_trade',
        'do_not_mail',
        'is_opt_out',
        'is_deleted',
        'contact_is_deleted',
      ),
      'date fields' => array(
        'birth_date',
        'deceased_date',
      ),
      'timestamp fields' => array(
        'created_date',
        'modified_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_contribution'] = array(
    'civicrm entity name' => 'contribution',
    'label property' => 'source',
    'permissions' => array(
      'view' => array(
        'access CiviContribute',
        'administer CiviCRM',
      ),
      'edit' => array(
        'edit contributions',
        'administer CiviCRM',
      ),
      'update' => array(
        'edit contributions',
        'administer CiviCRM',
      ),
      'create' => array(
        'edit contributions',
        'administer CiviCRM',
      ),
      'delete' => array(
        'edit contributions',
        'delete in CiviContribute',
        'administer CiviCRM',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-contribution',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contact_id_contact',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'contact_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'payment_processor',
          'target' => 'civicrm_payment_processor',
        ),
        array(
          'link_field' => 'id',
          'target' => 'civicrm_contribution',
        ),
      ),
      'option fields' => array(
        'financial_type_id',
        'contribution_status_id',
        'payment_instrument_id',
      ),
      'boolean fields' => array(
        'is_test',
        'is_pay_later',
      ),
      'date fields' => array(
        'cancel_date',
        'receipt_date',
        'thankyou_date',
        'receive_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_contribution_recur'] = array(
    'civicrm entity name' => 'contribution_recur',
    'label property' => 'id',
    'permissions' => array(
      'view' => array(
        'access CiviContribute',
        'administer CiviCRM',
      ),
      'edit' => array(
        'edit contributions',
        'administer CiviCRM',
      ),
      'update' => array(
        'edit contributions',
        'administer CiviCRM',
      ),
      'create' => array(
        'edit contributions',
        'administer CiviCRM',
      ),
      'delete' => array(
        'edit contributions',
        'delete in CiviContribute',
        'administer CiviCRM',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-contribution-recur',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contact_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'payment_processor_id',
          'target' => 'civicrm_payment_processor',
        ),
        array(
          'link_field' => 'financial_type_id',
          'target' => 'civicrm_contribution',
        ),
        array(
          'link_field' => 'campaign_id',
          'target' => 'civicrm_campaign',
        ),
      ),
      'option fields' => array(
        'financial_type_id',
        'contribution_status_id',
        'payment_instrument_id',
      ),
      'boolean fields' => array(
        'is_test',
        'is_email_receipt',
        'auto_renew',
      ),
      'date fields' => array(
        'create_date',
        'modified_date',
        'cancel_date',
        'end_date',
        'failure_retry_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_contribution_page'] = array(
    'civicrm entity name' => 'contribution_page',
    'label property' => 'title',
    'permissions' => array(
      'view' => array(
        'make online contributions',
      ),
      'edit' => array(
        'access CiviContribute',
        'administer CiviCRM',
      ),
      'update' => array(
        'access CiviContribute',
        'administer CiviCRM',
      ),
      'create' => array(
        'access CiviContribute',
        'administer CiviCRM',
      ),
      'delete' => array(
        'access CiviContribute',
        'administer CiviCRM',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-contribution-page',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'created_id_contact',
          'target' => 'civicrm_contact',
        ),
      ),
      'option fields' => array(
        'financial_type_id',
        'currency',
      ),
      'boolean fields' => array(
        'is_credit_card_only',
        'is_monetary',
        'is_recur',
        'is_confirm_enabled',
        'is_recur_interval',
        'is_recur_installments',
        'is_pay_later',
        'is_partial_payment',
        'is_allow_other_amount',
        'is_for_organization',
        'is_email_receipt',
        'is_active',
        'is_share',
        'is_billing_required',
      ),
      'date fields' => array(
        'start_date',
        'end_date',
        'created_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_country'] = array(
    'civicrm entity name' => 'country',
    'label property' => 'name',
    'permissions' => array(
      'view' => array(
        'view all contacts',
      ),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-country',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'boolean fields' => array(
        'is_province_abbreviated',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_email'] = array(
    'civicrm entity name' => 'email',
    'label property' => 'email',
    'permissions' => array(
      'view' => array(
        'view all contacts',
      ),
      'edit' => array(
        'edit all contacts',
      ),
      'update' => array(
        'edit all contacts',
      ),
      'create' => array(
        'edit all contacts',
      ),
      'delete' => array(
        'delete contacts',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-email',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contact_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'contact_id_contact',
          'target' => 'civicrm_contact',
        ),
      ),
      'option fields' => array(
        'location_type_id',
      ),
      'boolean fields' => array(
        'is_primary',
        'is_billing',
        'on_hold',
        'is_bulkmail',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_entity_tag'] = array(
    'civicrm entity name' => 'entity_tag',
    'label property' => 'tag_id',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-entity-tag',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'tag_id',
          'target' => 'civicrm_tag',
        ),
      ),
    ),
  );
  $civicrm_entity_info['civicrm_entity_financial_trxn'] = array(
    'civicrm entity name' => 'entity_financial_trxn',
    'label property' => 'id',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-entity-financial-trxn',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'financial_trxn_id',
          'target' => 'civicrm_financial_trxn',
        ),
      ),
    ),
  );
  $civicrm_entity_info['civicrm_financial_account'] = array(
    'civicrm entity name' => 'financial_account',
    'label property' => 'name',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-financial-account',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'option fields' => array(
        'financial_account_type_id',
      ),
      'link fields' => array(
        array(
          'link_field' => 'contact_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'parent_id',
          'target' => 'civicrm_financial_account',
        ),
      ),
      'date fields' => array(
        'trxn_date',
      ),
      'boolean fields' => array(
        'is_header_account',
        'is_deductible',
        'is_tax',
        'is_reserved',
        'is_active',
        'is_default',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_financial_trxn'] = array(
    'civicrm entity name' => 'financial_trxn',
    'label property' => 'id',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-financial-trxn',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'option fields' => array(
        'status_id',
      ),
      'link fields' => array(
        array(
          'link_field' => 'payment_processor_id',
          'target' => 'civicrm_payment_processor',
        ),
      ),
      'date fields' => array(
        'trxn_date',
      ),
    ),
  );

  //dirty check for whether financialType exists
  if (!method_exists('CRM_Contribute_PseudoConstant', 'contributionType')) {
    $civicrm_entity_info['civicrm_financial_type'] = array(
      'civicrm entity name' => 'financial_type',
      'label property' => 'description',
      'permissions' => array(
        'view' => array(
          'access CiviContribute',
          'administer CiviCRM',
        ),
        'edit' => array(
          'access CiviContribute',
          'administer CiviCRM',
        ),
        'update' => array(
          'access CiviContribute',
          'administer CiviCRM',
        ),
        'create' => array(
          'access CiviContribute',
          'administer CiviCRM',
        ),
        'delete' => array(
          'delete in CiviContribute',
          'administer CiviCRM',
        ),
      ),
      'theme' => array(
        'template' => 'civicrm-financial-type',
        'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
      ),
      'display suite' => array(
        'boolean fields' => array(
          'is_reserved',
          'is_active',
          'is_deductible',
        ),
      ),
    );
  }
  $civicrm_entity_info['civicrm_event'] = array(
    'civicrm entity name' => 'event',
    'label property' => 'title',
    'permissions' => array(
      'view' => array(
        'view event info',
      ),
      'edit' => array(
        'edit all events',
      ),
      'update' => array(
        'edit all events',
      ),
      'create' => array(
        'edit all events',
      ),
      'delete' => array(
        'edit all events',
        'delete in CiviEvent',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-event',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'created_id_contact',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'created_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'parent_event_id',
          'target' => 'civicrm_event',
        ),
      ),
      'option fields' => array(
        'event_type_id',
        'financial_type_id',
        'default_role_id',
      ),
      'boolean fields' => array(
        'is_public',
        'is_pay_later',
        'is_online_registration',
        'is_monetary',
        'is_map',
        'is_active',
        'is_show_location',
        'is_partial_payment',
        'is_multiple_registrations',
        'allow_same_participant_emails',
        'has_waitlist',
        'requires_approval',
        'is_template',
        'is_share',
        'is_confirm_enabled',
      ),
      'date fields' => array(
        'start_date',
        'end_date',
        'registration_start_date',
        'registration_end_date',
        'created_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_group'] = array(
    'civicrm entity name' => 'group',
    'label property' => 'name',
    'permissions' => array(
      'view' => array(
        'edit groups',
      ),
      'edit' => array(
        'edit groups',
      ),
      'update' => array(
        'edit groups',
      ),
      'create' => array(
        'edit groups',
      ),
      'delete' => array(
        'edit groups',
        'administer CiviCRM',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-group',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'created_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'created_id_contact',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'modified_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'modified_id_contact',
          'target' => 'civicrm_contact',
        ),
      ),
      'boolean fields' => array(
        'is_active',
        'is_hidden',
        'is_reserved',
      ),
      'timestamp fields' => array(
        'cached_date',
        'refresh_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_grant'] = array(
    'civicrm entity name' => 'grant',
    'label property' => '',
    'permissions' => array(
      'view' => array(
        'access CiviGrant',
        'administer CiviCRM',
      ),
      'edit' => array(
        'access CiviGrant',
        'edit grants',
      ),
      'update' => array(
        'access CiviGrant',
        'edit grants',
      ),
      'create' => array(
        'access CiviGrant',
        'edit grants',
      ),
      'delete' => array(
        'access CiviGrant',
        'edit grants',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-grant',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contact_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'contact_id_contact',
          'target' => 'civicrm_contact',
        ),
      ),
      'option fields' => array(
        'status_id',
        'financial_type_id',
        'grant_type_id',
      ),
      'boolean fields' => array(
        'grant_report_received',
      ),
      'date fields' => array(
        'application_received_date',
        'decision_date',
        'money_transfer_date',
        'grant_due_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_im'] = array(
    'civicrm entity name' => 'im',
    'label property' => 'name',
    'permissions' => array(
      'view' => array(
        'view all contacts',
      ),
      'edit' => array(
        'edit all contacts',
      ),
      'update' => array(
        'edit all contacts',
      ),
      'create' => array(
        'edit all contacts',
      ),
      'delete' => array(
        'delete contacts',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-im',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contact_id',
          'target' => 'civicrm_contact',
        ),
      ),
      'option fields' => array(
        'location_type_id',
        'provider_id',
      ),
      'boolean fields' => array(
        'is_primary',
        'is_billing',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_line_item'] = array(
    'civicrm entity name' => 'line_item',
    'label property' => 'label',
    'permissions' => array(
      'view' => array(
        'administer CiviCRM',
      ),
      'edit' => array(
        'administer CiviCRM',
      ),
      'update' => array(
        'administer CiviCRM',
      ),
      'create' => array(
        'administer CiviCRM',
      ),
      'delete' => array(
        'administer CiviCRM',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-line-item',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contribution_id',
          'target' => 'civicrm_contribution',
        ),
        array(
          'link_field' => 'price_field_id',
          'target' => 'civicrm_price_field',
        ),
        array(
          'link_field' => 'price_field_value_id',
          'target' => 'civicrm_price_field_value',
        ),
        array(
          'link_field' => 'financial_type_id',
          'target' => 'civicrm_financial_type',
        ),
      ),
    ),
  );
  $civicrm_entity_info['civicrm_loc_block'] = array(
    'civicrm entity name' => 'loc_block',
    'label property' => '',
    'permissions' => array(
      'view' => array(
        'administer CiviCRM',
      ),
      'edit' => array(
        'administer CiviCRM',
      ),
      'update' => array(
        'administer CiviCRM',
      ),
      'create' => array(
        'administer CiviCRM',
      ),
      'delete' => array(
        'administer CiviCRM',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-loc-block',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'address_id',
          'target' => 'civicrm_address',
        ),
        array(
          'link_field' => 'email_id',
          'target' => 'civicrm_email',
        ),
        array(
          'link_field' => 'phone_id',
          'target' => 'civicrm_phone',
        ),
        array(
          'link_field' => 'im_id',
          'target' => 'civicrm_im',
        ),
        array(
          'link_field' => 'address_2_id',
          'target' => 'civicrm_address',
        ),
        array(
          'link_field' => 'phone_2_id',
          'target' => 'civicrm_phone',
        ),
        array(
          'link_field' => 'email_2_id',
          'target' => 'civicrm_email',
        ),
        array(
          'link_field' => 'im_2_id',
          'target' => 'civicrm_im',
        ),
      ),
      'option fields' => array(),
      'boolean fields' => array(),
    ),
  );
  $civicrm_entity_info['civicrm_membership'] = array(
    'civicrm entity name' => 'membership',
    'permissions' => array(
      'view' => array(
        'access CiviMember',
      ),
      'edit' => array(
        'edit memberships',
        'access CiviMember',
      ),
      'update' => array(
        'edit memberships',
        'access CiviMember',
      ),
      'create' => array(
        'edit memberships',
        'access CiviMember',
      ),
      'delete' => array(
        'delete in CiviMember',
        'access CiviMember',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-membership',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contact_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'contact_id_contact',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'owner_membership_id',
          'target' => 'civicrm_membership',
        ),
        array(
          'link_field' => 'id',
          'target' => 'civicrm_membership',
        ),
      ),
      'option fields' => array(
        'membership_type_id',
        'status_id',
      ),
      'boolean fields' => array(
        'is_test',
        'is_pay_later',
        'is_override',
      ),
      'date fields' => array(
        'start_date',
        'end_date',
        'join_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_membership_payment'] = array(
    'civicrm entity name' => 'membership_payment',
    'permissions' => array(
      'view' => array(
        'access CiviMember',
        'access CiviContribute',
      ),
      'edit' => array(
        'access CiviMember',
        'access CiviContribute',
      ),
      'update' => array(
        'access CiviMember',
        'access CiviContribute',
      ),
      'create' => array(
        'access CiviMember',
        'access CiviContribute',
      ),
      'delete' => array(
        'delete in CiviMember',
        'access CiviMember',
        'access CiviContribute',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-membership-payment',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contribution_id',
          'target' => 'civicrm_contribution',
        ),
        array(
          'link_field' => 'membership_id',
          'target' => 'civicrm_membership',
        ),
      ),
      'option fields' => array(),
      'boolean fields' => array(),
    ),
  );
  $civicrm_entity_info['civicrm_membership_type'] = array(
    'civicrm entity name' => 'membership_type',
    'label property' => 'name',
    'permissions' => array(
      'view' => array(
        'access CiviMember',
      ),
      'edit' => array(
        'access CiviMember',
        'administer CiviCRM',
      ),
      'update' => array(
        'access CiviMember',
        'administer CiviCRM',
      ),
      'create' => array(
        'access CiviMember',
        'administer CiviCRM',
      ),
      'delete' => array(
        'delete in CiviMember',
        'access CiviMember',
        'administer CiviCRM',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-membership-type',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'member_of_contact_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'member_of_contact_id_contact',
          'target' => 'civicrm_contact',
        ),
      ),
      'option fields' => array(
        'financial_type_id',
      ),
      'boolean fields' => array(
        'is_active',
        'auto_renew',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_note'] = array(
    'civicrm entity name' => 'note',
    'label property' => 'subject',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-note',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contact_id',
          'target' => 'civicrm_contact',
        ),
      ),
      'date fields' => array(
        'modified_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_participant'] = array(
    'civicrm entity name' => 'participant',
    'label property' => 'source',
    'permissions' => array(
      'view' => array(
        'view event participants',
      ),
      'edit' => array(
        'edit event participants',
        'access CiviEvent',
      ),
      'update' => array(
        'edit event participants',
        'access CiviEvent',
      ),
      'create' => array(
        'edit event participants',
        'access CiviEvent',
      ),
      'delete' => array(
        'edit event participants',
        'access CiviEvent',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-participant',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contact_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'contact_id_contact',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'event_id',
          'target' => 'civicrm_event',
        ),
        array(
          'link_field' => 'event_id_event',
          'target' => 'civicrm_event',
        ),
        array(
          'link_field' => 'registered_by_id',
          'target' => 'civicrm_participant',
        ),
      ),
      'option fields' => array(
        'status_id',
        'role_id',
      ),
      'boolean fields' => array(
        'is_test',
        'is_pay_later',
        'must_wait',
      ),
      'date fields' => array(
        'register_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_participant_status_type'] = array(
    'civicrm entity name' => 'participant_status_type',
    'label property' => 'label',
    'permissions' => array(
      'view' => array(
        'view event participants',
      ),
      'edit' => array(
        'edit event participants',
        'access CiviEvent',
      ),
      'update' => array(
        'edit event participants',
        'access CiviEvent',
      ),
      'create' => array(
        'edit event participants',
        'access CiviEvent',
      ),
      'delete' => array(
        'edit event participants',
        'access CiviEvent',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-participant-status-type',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(),
      'option fields' => array(
        'visibility_id',
      ),
      'boolean fields' => array(
        'is_reserved',
        'is_active',
        'is_counted',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_participant_payment'] = array(
    'civicrm entity name' => 'participant_payment',
    'label property' => 'id',
    'permissions' => array(
      'view' => array(
        'administer CiviCRM',
      ),
      'edit' => array(
        'administer CiviCRM',
      ),
      'update' => array(
        'administer CiviCRM',
      ),
      'create' => array(
        'administer CiviCRM',
      ),
      'delete' => array(
        'administer CiviCRM',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-participant-payment',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'participant_id',
          'target' => 'civicrm_participant',
        ),
        array(
          'link_field' => 'contact_id',
          'target' => 'civicrm_contact',
        ),
      ),
    ),
  );
  $civicrm_entity_info['civicrm_payment_processor'] = array(
    'civicrm entity name' => 'payment_processor',
    'label property' => 'name',
    'permissions' => array(
      'view' => array(
        'administer CiviCRM',
      ),
      'edit' => array(
        'administer CiviCRM',
      ),
      'update' => array(
        'administer CiviCRM',
      ),
      'create' => array(
        'administer CiviCRM',
      ),
      'delete' => array(
        'administer CiviCRM',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-payment-processor',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'payment_processor_type_id',
          'target' => 'civicrm_payment_processor_type',
        ),
      ),
      'option fields' => array(),
      'boolean fields' => array(
        'is_active',
        'is_default',
        'is_test',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_payment_processor_type'] = array(
    'civicrm entity name' => 'payment_processor_type',
    'label property' => 'title',
    'permissions' => array(
      'view' => array(
        'administer CiviCRM',
      ),
      'edit' => array(
        'administer CiviCRM',
      ),
      'update' => array(
        'administer CiviCRM',
      ),
      'create' => array(
        'administer CiviCRM',
      ),
      'delete' => array(
        'administer CiviCRM',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-payment-processor-type',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(),
      'option fields' => array(),
      'boolean fields' => array(
        'is_active',
        'is_default',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_phone'] = array(
    'civicrm entity name' => 'phone',
    'label property' => 'phone',
    'permissions' => array(
      'view' => array(
        'view all contacts',
      ),
      'edit' => array(
        'edit all contacts',
      ),
      'update' => array(
        'edit all contacts',
      ),
      'create' => array(
        'edit all contacts',
      ),
      'delete' => array(
        'delete contacts',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-phone',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contact_id',
          'target' => 'civicrm_contact',
        ),
      ),
      'option fields' => array(
        'location_type_id',
        'mobile_provider_id',
        'phone_type_id',
      ),
      'boolean fields' => array(
        'is_primary',
        'is_billing',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_pledge'] = array(
    'civicrm entity name' => 'pledge',
    'permissions' => array(
      'view' => array(
        'access CiviPledge',
      ),
      'edit' => array(
        'edit pledges',
      ),
      'update' => array(
        'edit pledges',
      ),
      'create' => array(
        'edit pledges',
      ),
      'delete' => array(
        'edit pledges',
        'administer CiviCRM',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-pledge',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'date fields' => array(
        'start_date',
        'end_date',
        'cancel_date',
        'modified_date',
        'created_date',
        'acknowledge_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_pledge_payment'] = array(
    'civicrm entity name' => 'pledge_payment',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-pledge-payment',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'date fields' => array(
        'scheduled_date',
        'reminder_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_price_set'] = array(
    'civicrm entity name' => 'price_set',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-price-set',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'boolean fields' => array(
        'is_active',
        'is_quick_config',
        'is_reserved',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_price_field'] = array(
    'civicrm entity name' => 'price_field',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-price-field',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'price_set_id',
          'target' => 'civicrm_price_set',
        ),
      ),
      'option fields' => array(
        'visibility_id',
      ),
      'boolean fields' => array(
        'is_enter_qty',
        'is_display_amounts',
        'is_active',
        'is_required',
      ),
      'date fields' => array(
        'active_on',
        'expire_on',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_price_field_value'] = array(
    'civicrm entity name' => 'price_field_value',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-price-field-value',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'price_field_id',
          'target' => 'civicrm_price_field',
        ),
        array(
          'link_field' => 'membership_type_id',
          'target' => 'civicrm_membership_type',
        ),
      ),
      'option fields' => array(
        'financial_type_id',
      ),
      'boolean fields' => array(
        'is_default',
        'is_active',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_recurring_entity'] = array(
    'civicrm entity name' => 'recurring_entity',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-recurring-entity',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(),
      'option fields' => array(),
      'boolean fields' => array(),
    ),
  );
  $civicrm_entity_info['civicrm_relationship'] = array(
    'civicrm entity name' => 'relationship',
    'label property' => 'description',
    'permissions' => array(
      'view' => array(
        'view all contacts',
      ),
      'edit' => array(
        'edit all contacts',
      ),
      'update' => array(
        'edit all contacts',
      ),
      'create' => array(
        'edit all contacts',
      ),
      'delete' => array(
        'edit all contacts',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-relationship',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contact_id_a',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'contact_id_a_contact',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'contact_id_b',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'contact_id_b_contact',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'relationship_type_id',
          'target' => 'civicrm_relationship_type',
        ),
      ),
      'boolean fields' => array(
        'is_active',
        'is_permission_a_b',
        'is_permission_b_a',
      ),
      'date fields' => array(
        'start_date',
        'end_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_relationship_type'] = array(
    'civicrm entity name' => 'relationship_type',
    'label property' => 'description',
    'permissions' => array(
      'view' => array(
        'administer CiviCRM',
      ),
      'edit' => array(
        'administer CiviCRM',
      ),
      'update' => array(
        'administer CiviCRM',
      ),
      'create' => array(
        'administer CiviCRM',
      ),
      'delete' => array(
        'administer CiviCRM',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-relationship-type',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'option fields' => array(
        'contact_sub_type_a',
        'contact_sub_type_b',
      ),
      'boolean fields' => array(
        'is_reserved',
        'is_active',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_survey'] = array(
    'civicrm entity name' => 'survey',
    'label property' => 'title',
    'permissions' => array(
      'view' => array(
        'administer CiviCampaign',
      ),
      'edit' => array(
        'administer CiviCampaign',
      ),
      'update' => array(
        'administer CiviCampaign',
      ),
      'create' => array(
        'administer CiviCampaign',
      ),
      'delete' => array(
        'administer CiviCampaign',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-survey',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'campaign_id',
          'target' => 'civicrm_campaign',
        ),
        array(
          'link_field' => 'created_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'last_modified_id',
          'target' => 'civicrm_contact',
        ),
      ),
      'option fields' => array(
        'activity_type_id',
      ),
      'boolean fields' => array(
        'is_active',
        'is_default',
        'bypass_confirm',
        'is_share',
      ),
      'date fields' => array(
        'created_date',
        'last_modified_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_tag'] = array(
    'civicrm entity name' => 'tag',
    'label property' => 'name',
    'permissions' => array(
      'view' => array(
        'administer Tagsets',
      ),
      'edit' => array(
        'administer Tagsets',
      ),
      'update' => array(
        'administer Tagsets',
      ),
      'create' => array(
        'administer Tagsets',
      ),
      'delete' => array(
        'administer Tagsets',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-tag',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'created_id',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'created_id_contact',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'parent_id',
          'target' => 'civicrm_tag',
        ),
      ),
      'boolean fields' => array(
        'is_reserved',
        'is_tagset',
        'is_selectable',
      ),
      'date fields' => array(
        'created_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_custom_field'] = array(
    'civicrm entity name' => 'custom_field',
    'label property' => 'label',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-custom-field',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'custom_group_id',
          'target' => 'civicrm_custom_group',
        ),
      ),
      'boolean fields' => array(
        'is_view',
        'is_active',
        'is_required',
        'is_searchable',
        'is_search_range',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_custom_group'] = array(
    'civicrm entity name' => 'custom_group',
    'label property' => 'title',
    'permissions' => array(
      'view' => array(),
      'edit' => array(),
      'update' => array(),
      'create' => array(),
      'delete' => array(),
    ),
    'theme' => array(
      'template' => 'civicrm-custom-group',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'created_id_contact',
          'target' => 'civicrm_contact',
        ),
        array(
          'link_field' => 'membership_type_id',
          'target' => 'civicrm_membership_type',
        ),
      ),
      'boolean fields' => array(
        'is_multiple',
        'is_active',
        'collapse_display',
        'collapse_adv_display',
        'is_reserved',
      ),
      'date fields' => array(
        'created_date',
      ),
    ),
  );
  $civicrm_entity_info['civicrm_website'] = array(
    'civicrm entity name' => 'website',
    'label property' => 'url',
    'permissions' => array(
      'view' => array(
        'view all contacts',
      ),
      'edit' => array(
        'edit all contacts',
      ),
      'update' => array(
        'edit all contacts',
      ),
      'create' => array(
        'edit all contacts',
      ),
      'delete' => array(
        'delete contacts',
      ),
    ),
    'theme' => array(
      'template' => 'civicrm-website',
      'path' => drupal_get_path('module', 'civicrm_entity') . '/templates',
    ),
    'display suite' => array(
      'link fields' => array(
        array(
          'link_field' => 'contact_id',
          'target' => 'civicrm_contact',
        ),
      ),
      'option fields' => array(
        'website_type_id',
      ),
    ),
  );
  return $civicrm_entity_info;
}

/**
 * We gather a list entities that are available, either as hardcoded in this module, or provided by the hook
 */
function _civicrm_entity_available_entities() {
  $civicrm_entity_info = civicrm_entity_get_supported_entity_info();
  $whitelist = array();
  foreach ($civicrm_entity_info as $drupal_entity => $data) {
    if (isset($data['civicrm entity name'])) {
      $whitelist[$drupal_entity] = $data['civicrm entity name'];
    }
  }
  _civicrm_enabled_entity_alter_whitelist($whitelist);
  return $whitelist;
}

/**
 * Whitelist of enabled entities. We don't have a compelling reason for not including all entities
 * but some entities are fairly non-standard and of course the rule hook would instantiate rules
 * more often if all were enabled.
 *
 * The whitelist approach is mostly out of caution
 *
 * @return array of enabled entities keyed by the drupal entity name
 */
function _civicrm_entity_enabled_entities() {
  $available_entities = _civicrm_entity_available_entities();
  $available_entities_option_set = _civicrm_entity_available_entities_get_option_set($available_entities);
  $selected_entities = variable_get('civicrm_entity_admin_enabled_entities', $available_entities_option_set);
  $enabled_entities = array();
  foreach ($available_entities as $entity_type => $api_entity) {
    if (!empty($selected_entities[$entity_type])) {
      $enabled_entities[$entity_type] = $api_entity;
    }
  }
  return $enabled_entities;
}

/**
 * Build an option set array used by the configure CiviCRM Entity form, and various submodules
 *
 * @param $available_entities
 *
 * @return array
 */
function _civicrm_entity_available_entities_get_option_set($available_entities) {
  $available_entities_option_set = array();
  if (!empty($available_entities)) {
    foreach ($available_entities as $entity_type => $api_entity) {
      $available_entities_option_set[$entity_type] = $entity_type;
    }
  }
  return $available_entities_option_set;
}

/**
 * Allow extensions to alter entities
 * @param array $whitelist
 *
 * @return mixed
 */
function _civicrm_enabled_entity_alter_whitelist(&$whitelist) {
  if (!civicrm_initialize()) {
    return;
  }
  $codeVersion = explode('.', CRM_Utils_System::version());

  // if db.ver < code.ver, time to upgrade
  if (version_compare($codeVersion[0] . '.' . $codeVersion[1], 5.28) >= 0) {
    return CRM_Utils_Hook::singleton()
      ->invoke([
      'entities',
    ], $whitelist, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, 'civicrm_alter_drupal_entities');
  }
  elseif (version_compare($codeVersion[0] . '.' . $codeVersion[1], 4.5) >= 0) {
    return CRM_Utils_Hook::singleton()
      ->invoke(1, $whitelist, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, 'civicrm_alter_drupal_entities');
  }
  else {
    return CRM_Utils_Hook::singleton()
      ->invoke(1, $whitelist, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, 'civicrm_alter_drupal_entities');
  }
}

/**
 * Provide label (column) for each entity types - default to id if nothing specified.
 *
 * @TODO Use the CiviCRM 4.5 getlist function - possibly ported into this module to support 4.4
 *
 * @see http://api.drupal.org/api/drupal/modules!system!system.api.php/function/hook_entity_info/7
 *
 * @param $entity
 * @return string
 */
function _civicrm_entity_labels($entity) {
  $civicrm_entity_info = civicrm_entity_get_supported_entity_info();
  $labels = array();
  foreach ($civicrm_entity_info as $drupal_entity => $data) {
    if (isset($data['label property']) && $data['label property'] != '') {
      $labels[$drupal_entity] = $data['label property'];
    }
  }
  _civicrm_enabled_entity_alter_labels($labels);
  return isset($labels[$entity]) ? $labels[$entity] : 'id';
}

/**
 * @param array $labels
 *
 * @return mixed
 */
function _civicrm_enabled_entity_alter_labels(&$labels) {
  if (!civicrm_initialize()) {
    return;
  }
  $codeVersion = explode('.', CRM_Utils_System::version());

  // if db.ver < code.ver, time to upgrade
  if (version_compare($codeVersion[0] . '.' . $codeVersion[1], 5.28) >= 0) {
    return CRM_Utils_Hook::singleton()
      ->invoke([
      'labels',
    ], $labels, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, 'civicrm_alter_drupal_entity_labels');
  }
  elseif (version_compare($codeVersion[0] . '.' . $codeVersion[1], 4.5) >= 0) {
    return CRM_Utils_Hook::singleton()
      ->invoke(1, $labels, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, 'civicrm_alter_drupal_entity_labels');
  }
  else {
    return CRM_Utils_Hook::singleton()
      ->invoke(1, $labels, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, CRM_Core_DAO::$_nullObject, 'civicrm_alter_drupal_entity_labels');
  }
}

/**
 * Here we declare Selected CiviCRM entities fields to Drupal.
 *
 * Some trickiness here as declaring the 'schema' via our special civi
 * schema function seems to cause fields to be declared twice if we us
 * property_info rather than property_info_alter.
 *
 * At the moment civicrm_relationship_type is the only entity being
 * managed through 'our' schema.
 *
 * @param $info
 *
 * @return
 */
function civicrm_entity_entity_property_info_alter(&$info) {
  if (!civicrm_initialize(TRUE)) {
    return;
  }

  // We'll start with a few basic entities but we could get them the
  // same way the API explorer does.
  $entities = _civicrm_entity_enabled_entities();
  foreach ($entities as $drupal_entity => $civicrm_entity) {
    $info[$drupal_entity]['properties'] = _civicrm_entity_getproperties($civicrm_entity, 'property_info');

    // $info[$drupal_entity]['bundles'] = array();
  }

  // This makes the drupal user available when chaining from a rule.
  $info['civicrm_contact']['properties']['civicrm_user'] = array(
    'label' => 'Drupal User',
    'description' => 'Drupal User for contact',
    'type' => 'user',
    'translateable' => FALSE,
    'getter callback' => 'civicrm_entity_contact_user_get',
    'setter callback' => 'civicrm_entity_contact_user_set',
  );

  // Attach a CiviCRM Contact property to drupal users.
  $info['user']['properties']['civicrm_contact'] = array(
    'label' => 'CiviCRM Contact',
    'description' => 'CiviCRM Contact for user',
    'type' => 'civicrm_contact',
    'field' => FALSE,
    'translatable' => FALSE,
    'getter callback' => 'civicrm_entity_user_contact_get',
  );
  return $info;
}

/**
 *  Implements hook_field_extra_fields_alter().
 *
 *  put civicrm properties on the manage fields form for weight based ordering of edit form elements
 *
 * @param $info
 */
function civicrm_entity_field_extra_fields_alter(&$info) {
  if (!civicrm_initialize(TRUE)) {
    return;
  }
  $entities = _civicrm_entity_enabled_entities();
  foreach ($entities as $drupal_entity => $civicrm_entity) {
    $properties[$drupal_entity]['properties'] = _civicrm_entity_getproperties($civicrm_entity, 'property_info');
    if (!empty($info[$drupal_entity][$drupal_entity]['display'])) {
      foreach ($info[$drupal_entity][$drupal_entity]['display'] as $name => $props) {
        $info[$drupal_entity][$drupal_entity]['form'][$name] = $props;
      }
    }
  }
}

/**
 * Calculate fields for entities
 *
 * @param $civicrm_entity
 * @param string $context
 *
 * @return array
 */
function _civicrm_entity_getproperties($civicrm_entity, $context = '') {
  $info = array();
  if ($civicrm_entity == 'contact') {
    $info['civi_user'] = array(
      'label' => 'Drupal User',
      'type' => 'user',
      'field' => FALSE,
      'translateable' => FALSE,
      'getter callback' => 'civicrm_entity_contact_user_get',
      'setter callback' => 'civicrm_entity_contact_user_set',
    );
  }
  $fields = civicrm_api($civicrm_entity, 'getfields', array(
    'version' => 3,
    'action' => 'create',
  ));
  foreach ($fields['values'] as $fieldname => $field_specs) {

    // Type is empty for custom fields - we should sort that out but
    // skipping for now we are only doing 'integers' at this stage.
    $types = array(
      1 => 'integer',
      2 => 'text',
      32 => 'text',
      16 => 'integer',
      4 => 'datetime',
      8 => 'datetime',
      12 => 'datetime',
      256 => 'datetime',
      512 => 'decimal',
      1024 => 'decimal',
    );
    if (!empty($field_specs['type']) && array_key_exists($field_specs['type'], $types)) {

      //determine the label of the field
      $label = _civicrm_entity_get_title($field_specs);
      $info[$fieldname] = array(
        'label' => $label,
        'type' => $types[$field_specs['type']],
        'sanitize' => 'filter_xss',
        'setter callback' => 'entity_property_verbatim_set',
      );
      if (!empty($field_specs['api.required']) || !empty($field_specs['is_required'])) {
        $info[$fieldname]['required'] = TRUE;
      }

      // add field description to metadata
      if (!empty($field_specs['description'])) {
        $info[$fieldname]['description'] = $field_specs['description'];
      }

      // add field maxlength to metadata
      if (!empty($field_specs['maxlength'])) {
        $info[$fieldname]['maxlength'] = $field_specs['maxlength'];
      }

      // add field text_length to metadata
      if (!empty($field_specs['text_length'])) {

        // just use the entity maxlength instead of creating another index
        $info[$fieldname]['maxlength'] = $field_specs['text_length'];
      }

      // add field size to metadata
      if (!empty($field_specs['size'])) {
        $info[$fieldname]['size'] = $field_specs['size'];
      }

      // add field dataPattern to metadata
      if (!empty($field_specs['dataPattern'])) {
        $info[$fieldname]['dataPattern'] = $field_specs['dataPattern'];
      }

      // add field is_view to metadata
      if (!empty($field_specs['is_view'])) {
        $info[$fieldname]['is_view'] = $field_specs['is_view'];
      }

      // add field default value to metadata
      if (isset($field_specs['default_value'])) {
        $info[$fieldname]['default_value'] = $field_specs['default_value'];
      }

      // add field rows to metadata
      if (isset($field_specs['rows'])) {
        $info[$fieldname]['rows'] = $field_specs['rows'];
      }

      // add field rows to metadata
      if (isset($field_specs['cols'])) {
        $info[$fieldname]['cols'] = $field_specs['cols'];
      }
      if ($field_specs['type'] == 16) {
        $info[$fieldname]['size'] = 'tiny';
      }
      if ($types[$field_specs['type']] == 'datetime') {

        // why were we unsetting type?
        unset($info[$fieldname]['type']);

        //$info[$fieldname]['type'] = 'text';
        $info[$fieldname]['mysql_type'] = 'datetime';
        $info[$fieldname]['tz_handling'] = 'site';
        $info[$fieldname]['offset_field'] = '';
        $info[$fieldname]['related_fields'] = array();
        switch ($field_specs['type']) {
          case 4:
            $info[$fieldname]['granularity'] = array(
              'year',
              'month',
              'day',
            );
            break;
          case 8:
            $info[$fieldname]['granularity'] = array(
              'hour',
              'minute',
              'second',
            );
          case 12:
            $info[$fieldname]['granularity'] = array(
              'year',
              'month',
              'day',
              'hour',
              'minute',
              'second',
            );
        }
      }

      // This is a semi-reliable way of distinguishing 'real' fields
      // from pseudo fields and custom fields and impacts on views
      if (!empty($field_specs['name'])) {
        $info[$fieldname]['schema field'] = $field_specs['name'];
      }

      //widget code
      $info[$fieldname]['widget'] = civicrm_entity_get_field_widget($field_specs, $civicrm_entity);

      // We will add contact as a related entity for FK references to
      // contact. This could be expanded to all FKs e.g event_id in
      // Participant. Could load the event at the moment we are being
      // cautious.
      if (CRM_Utils_Array::value('FKClassName', $field_specs)) {
        $fks = _civicrm_entity_chained_fks();
        if (array_key_exists($field_specs['FKClassName'], $fks)) {
          $fks_entity = $fks[$field_specs['FKClassName']];
          $info[$fieldname . '_' . $fks_entity] = array(
            'label' => _civicrm_entity_get_title($field_specs),
            'type' => 'civicrm_' . $fks_entity,
            'property_info' => array(
              'field' => $fieldname,
              'entity' => $fks_entity,
            ),
            'getter callback' => 'civicrm_entity_metadata_civicrm_entity_get_properties',
          );
        }
      }

      // @TODO We are treating contact as the only possible entity
      // which is not great - need to figure out better approach - can
      // we have more than one? Define 'civicrm_entity'?
      if ($fieldname == 'entity_id') {
        $fks_entity = 'contact';
        $info[$fieldname . '_' . $fks_entity] = array(
          'label' => _civicrm_entity_get_title($field_specs),
          'type' => 'civicrm_' . $fks_entity,
          'property_info' => array(
            'field' => $fieldname,
            'entity' => $fks_entity,
          ),
          'getter callback' => 'civicrm_entity_metadata_civicrm_entity_get_properties',
        );
      }
      if (!empty($field_specs['options'])) {

        // $info[$fieldname]['type'] = 'list<integer>';
        $info[$fieldname]['options list'] = '_civicrm_entity_rules_attach_options';
        $info[$fieldname]['options data'] = $field_specs['options'];
        if ($context == 'property_info') {
          $info[$fieldname]['property defaults']['options list'] = $field_specs['options'];
        }
      }
      $info['type'] = array(
        'label' => t('Type'),
        'description' => t('Dummy field for bundle key'),
        'type' => 'token',
        'setter callback' => 'entity_property_verbatim_set',
        'required' => FALSE,
        'property defaults' => array(
          'civicrm_' . strtolower($civicrm_entity),
        ),
      );
    }
  }

  // 4.7 does not return metadata for the contact_id property, with a Contact API getfields call, Action => create
  if ($civicrm_entity == 'contact') {
    if (!isset($info['contact_id_contact'])) {
      $info['contact_id_contact'] = array(
        'label' => 'Contact ID',
        'type' => 'civicrm_contact',
        'property_info' => array(
          'field' => 'id',
          'entity' => 'contact',
        ),
        'getter callback' => 'civicrm_entity_metadata_civicrm_entity_get_properties',
      );
    }
  }
  if ($civicrm_entity == 'activity') {
    $info['activity_type_id']['required'] = TRUE;
  }
  return $info;
}

/**
 * Utility function that takes the provided field specs from a getfields call and builds
 * a widget property array on the property metadata info
 *
 * @param $field_spec
 *
 * @return array
 */
function civicrm_entity_get_field_widget($field_spec, $civicrm_entity) {
  $widget = array();
  if (isset($field_spec['name']) && $field_spec['name'] == 'id') {
    $widget = array(
      'widget' => 'hidden',
    );
  }
  if (!isset($field_spec['type'])) {
    $widget = array(
      'widget' => 'hidden',
    );
  }
  $field_type = 'standard';

  // "normal" properties
  if (isset($field_spec['html']['type'])) {
    $html_type = $field_spec['html']['type'];
    $field_type = 'standard';
  }
  elseif (isset($field_spec['html_type'])) {
    $html_type = $field_spec['html_type'];
    $field_type = 'custom';
  }
  if (isset($html_type)) {
    switch ($html_type) {
      case 'Text':
        $widget = array(
          'widget' => 'textfield',
          'civicrm_field_type' => $field_type,
        );
        break;
      case 'Select':
        $widget = array(
          'widget' => 'select',
          'civicrm_field_type' => $field_type,
        );
        $widget['options'] = civicrm_entity_get_field_options($field_spec['name'], $civicrm_entity);
        break;
      case 'Radio':
        $widget = array(
          'widget' => 'radios',
          'civicrm_field_type' => $field_type,
        );
        $widget['options'] = civicrm_entity_get_field_options($field_spec['name'], $civicrm_entity);
        break;
      case 'Autocomplete-Select':
        $widget = array(
          'widget' => 'textfield',
          'civicrm_field_type' => $field_type,
        );
        break;
      case 'CheckBox':
        $options = civicrm_entity_get_field_options($field_spec['name'], $civicrm_entity);
        if ($field_type == 'custom' && count($options) > 1) {
          $widget = array(
            'widget' => 'checkboxes',
            'civicrm_field_type' => $field_type,
            'options' => $options,
          );
        }
        else {
          $widget = array(
            'widget' => 'checkbox',
            'civicrm_field_type' => $field_type,
          );
        }
        break;
      case 'RichTextEditor':
        $widget = array(
          'widget' => 'text_format',
          'civicrm_field_type' => $field_type,
        );
        break;
      case 'TextArea':
        if (!empty($field_spec['description']) && strpos($field_spec['description'], 'Text and html allowed.') !== FALSE) {
          $widget = array(
            'widget' => 'text_format',
            'civicrm_field_type' => $field_type,
          );
        }
        else {
          $widget = array(
            'widget' => 'textarea',
            'civicrm_field_type' => $field_type,
          );
        }
        break;
      default:
        $widget = array(
          'widget' => 'textfield',
          'civicrm_field_type' => $field_type,
        );
        break;
    }
  }
  else {
    $widget = array(
      'widget' => 'textfield',
    );
  }

  // some date field handling
  if (isset($field_spec['type'])) {
    switch ($field_spec['type']) {
      case 4:
        $widget = array(
          'widget' => 'date_select',
          'format' => 'Y:m:d',
        );
        break;
      case 8:
        $widget = array(
          'widget' => 'date_select',
          'format' => 'H:i:s',
        );
        break;
      case 12:
        $widget = array(
          'widget' => 'date_select',
          'format' => 'Y:m:d H:i:s',
        );
        break;
    }
  }

  // FK Reference field handling
  if (!empty($field_spec['FKApiName'])) {
    $widget_subtype = civicrm_entity_fk_entities_to_produce_widget_for($field_spec['FKApiName']);
    if ($widget_subtype) {
      $widget = array(
        'widget' => 'civi_fk_reference',
        'subtype' => $widget_subtype,
        'entity' => $field_spec['FKApiName'],
      );
    }
  }
  if ($civicrm_entity == 'participant' && $field_spec['name'] == 'event_id') {
    $widget = array(
      'widget' => 'civi_fk_reference',
      'subtype' => 'select',
      'entity' => 'Event',
    );
  }

  // some one off handle for properties of some entities that don't provide html type information
  $entities_to_alter = array(
    'address',
    'phone',
    'email',
    'im',
    'relationship',
    'membership_type',
    'activity',
  );
  if (in_array(strtolower($civicrm_entity), $entities_to_alter)) {
    $checkbox_fields = array(
      'is_primary',
      'is_billing',
      'on_hold',
      'is_bulk_mail',
      'is_active',
      'is_permission_a_b',
      'is_permission_b_a',
      'is_deleted',
    );
    if (in_array($field_spec['name'], $checkbox_fields)) {
      $widget = array(
        'widget' => 'checkbox',
        'civicrm_field_type' => 'standard',
      );
    }
  }
  if ($civicrm_entity == 'membership_type' && !empty($field_spec['name']) && $field_spec['name'] == 'relationship_type_id') {
    $widget = array(
      'widget' => 'civi_fk_reference',
      'subtype' => civicrm_entity_fk_entities_to_produce_widget_for('RelationshipType'),
      'entity' => 'RelationshipType',
    );
  }
  if ($civicrm_entity == 'activity' && !empty($field_spec['name']) && $field_spec['name'] == 'result') {
    $widget = array(
      'widget' => 'select',
      'civicrm_field_type' => $field_type,
    );
    $widget['options'] = civicrm_entity_get_field_options($field_spec['name'], $civicrm_entity);
  }
  if ($civicrm_entity == 'contact' && !empty($field_spec['name']) && $field_spec['name'] == 'communication_style_id') {
    $widget = array(
      'widget' => 'select',
      'civicrm_field_type' => $field_type,
    );
    $widget['options'] = civicrm_entity_get_field_options($field_spec['name'], $civicrm_entity);
  }
  return $widget;
}

/**
 * Call back function used to get select list options for some FK Reference fields.
 *
 * @param $civicrm_entity
 * @return array
 */
function _civicrm_entity_get_entities_list($civicrm_entity) {
  civicrm_initialize();
  try {
    $result = civicrm_api3($civicrm_entity, 'get', array(
      'options' => array(
        'limit' => 100,
      ),
    ));
  } catch (CiviCRM_API3_Exception $e) {
    return array(
      '',
    );
  }
  $entities = array();
  if ($result['count']) {
    foreach ($result['values'] as $id => $data) {
      switch ($civicrm_entity) {
        case 'RelationshipType':
          $entities[$id] = $data['label_a_b'] . ' / ' . $data['label_b_a'];
          break;
        case 'FinancialType':
          $entities[$id] = $data['name'];
          break;
        case 'Campaign':
          $entities[$id] = $data['title'];
          break;
        case 'Event':
          if (empty($data['is_template'])) {
            $entities[$id] = $data['event_title'];
          }
          break;
      }
    }
  }
  return $entities;
}

/**
 * Utility function to get the options for a civicrm options field for the widget metadata
 *
 * @param $field_name
 * @param $civicrm_entity
 * @return array
 */
function civicrm_entity_get_field_options($field_name, $civicrm_entity) {
  $result = civicrm_api($civicrm_entity, 'getoptions', array(
    'sequential' => 1,
    'version' => 3,
    'field' => $field_name,
  ));
  $options = array(
    NULL => '- No Value -',
  );
  if (!$result['is_error']) {
    foreach ($result['values'] as $option) {
      $options[$option['key']] = $option['value'];
    }
  }
  return $options;
}

/**
 * Utility function to return which entities we're allowing for using a special FK FAPI Widget for some FK Reference fields
 *
 * @return array
 */
function civicrm_entity_fk_entities_to_produce_widget_for($entity_type) {
  switch ($entity_type) {
    case 'Contact':
    case 'Event':
      return 'autocomplete';
    case 'Campaign':
    case 'RelationshipType':
    case 'FinancialType':
      return 'select';
  }
  return FALSE;
}

/**
 * Please document this function.
 */
function _civicrm_entity_chained_fks() {
  return array(
    'CRM_Contact_DAO_Contact' => 'contact',
    'CRM_Event_DAO_Event' => 'event',
  );
}

/**
 * Title callback
 *
 * @param $field_specs
 * @param string $entity_type
 * @return string
 */
function _civicrm_entity_get_title($field_specs, $entity_type = '') {
  if (!empty($entity_type)) {
    $label_field = _civicrm_entity_labels($entity_type);
  }
  else {
    $label_field = 'title';
  }
  if (empty($field_specs[$label_field])) {
    if (array_key_exists('label', $field_specs)) {
      $label_field = 'label';
    }
    else {
      if (array_key_exists('name', $field_specs)) {
        $label_field = 'name';
      }
    }
  }
  $label = $field_specs[$label_field];
  if (!empty($field_specs['groupTitle'])) {
    $label = $field_specs['groupTitle'] . ': ' . $label;
  }
  return empty($label) ? 'Title not defined in schema' : $label;
}

/**
 * Label callback for civicrm_contact entity type.
 *
 *   drupal_alter('civicrm_entity_' . $entity, $, $alterable2, $context);
 *
 * @param $entity
 * @param $entity_type
 *
 * @return null|string
 */
function civicrm_entity_civicrm_contact_label_callback($entity, $entity_type) {
  $label = isset($entity->display_name) ? $entity->display_name : '';
  $contact_label_setting = variable_get('civicrm_entity_contact_label_format', 'legacy');
  if ($contact_label_setting == 'legacy') {

    // drupal_alter('civicrm_entity_contact_label', $label, $entity);
    if (isset($entity->email) && !empty($entity->email)) {
      $label = t('!label <!email>', [
        '!label' => $label,
        '!email' => $entity->email,
      ]);
    }
    elseif (isset($entity->phone) && !empty($entity->phone)) {
      $label = t('!label <!phone>', [
        '!label' => $label,
        '!phone' => $entity->phone,
      ]);
    }
    elseif (isset($entity->im) && !empty($entity->im)) {
      $label = t('!label <!im>', [
        '!label' => $label,
        '!im' => $entity->im,
      ]);
    }
  }
  elseif ($contact_label_setting == 'display_name_email' && !empty($entity->email)) {
    $label = t('!label <!email>', [
      '!label' => $label,
      '!email' => $entity->email,
    ]);
  }
  elseif ($contact_label_setting == 'display_name_phone' && !empty($entity->phone)) {
    $label = t('!label <!phone>', [
      '!label' => $label,
      '!phone' => $entity->phone,
    ]);
  }
  elseif ($contact_label_setting == 'display_name_im' && !empty($entity->im)) {
    $label = t('!label <!im>', [
      '!label' => $label,
      '!im' => $entity->im,
    ]);
  }
  return $label;
}

/**
 * Implement getter callback.
 *
 * NB this is in a separate file called callbacks.inc in entity module
 * - I couldn't see how it was loaded so maybe the name has some
 * magic?
 *
 * MH - Skvare -- This function should not use the CiviCRM api, but instead the Drupal Entity API to load and return the entity object
 *
 * @param $data
 * @param $options
 * @param $name
 * @param $type
 * @param $info
 *
 * @return object
 */
function civicrm_entity_metadata_civicrm_entity_get_properties($data, $options, $name, $type, $info) {
  if (isset($info['property_info']['entity']) && isset($data->{$info['property_info']['field']})) {

    /* $entity = civicrm_api($info['property_info']['entity'], 'get', array(
         'version' => 3,
         'id' => $data->$info['property_info']['field'],
         'sequential' => 1,
       ));
       return (object) $entity['values'][0];*/
    return entity_load_single('civicrm_' . $info['property_info']['entity'], $data->{$info['property_info']['field']});
  }
}

/**
 * Condition Drupal User Account exists for contact.
 *
 * @param array $contact
 *   Contact array.
 *
 * @return object
 *   Drupal user object if success, FALSE on fail.
 */
function civicrm_entity_user_exists($contact) {
  return civicrm_entity_action_load_user($contact);
}

/**
 * Condition Drupal User Account can be created for contact (creates contact).
 *
 * @param array $contact
 *   contact array
 *
 * @return object
 *   Drupal user object if success, FALSE on Fail
 */
function civicrm_entity_user_creatable($contact) {
  return civicrm_entity_action_create_user($contact, TRUE);
}

/**
 * Condition Drupal User Account can be created or exists for contact.
 *
 * Creates contact if appropriate.
 *
 * @param array $contact
 *   contact array
 *
 * @return mixed
 *   Drupal user object if success, FALSE on fail.
 */
function civicrm_entity_user_exists_or_creatable($contact) {
  return civicrm_entity_action_load_create_user($contact);
}

/**
 * Condition Contact is in Group
 */
function civicrm_entity_contact_is_in_group($contact, $group_id) {
  if (!civicrm_initialize()) {
    return;
  }
  try {
    $contact_id = $contact->id;
    $params = array(
      'sequential' => 1,
      'contact_id' => $contact_id,
      'group_id' => $group_id,
    );
    $get_result = civicrm_api3('GroupContact', 'Get', $params);
    if (!empty($get_result['count'])) {
      $group = entity_load_single('civicrm_group', $group_id);
      return $group;
    }
  } catch (CiviCRM_API3_Exception $e) {
    drupal_set_message($e
      ->getMessage());
  }
  return FALSE;
}

/**
 * Condition Contact is of Sub type
 */
function civicrm_entity_contact_is_subtype($contact, $subtype) {
  $contact_subtypes = explode(CRM_Core_DAO::VALUE_SEPARATOR, $contact->contact_sub_type);
  if (!empty($contact_subtypes) && in_array($subtype, $contact_subtypes)) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Given a contact object return the Drupal user.
 *
 * @param StdClass $entity
 *   Contact Std Object
 *
 * @return object
 *   Drupal user object.
 */
function civicrm_entity_action_load_user($entity) {
  $domain_id = civicrm_api('domain', 'getvalue', array(
    'version' => 3,
    'return' => 'id',
    'current_domain' => TRUE,
  ));
  $params = array(
    'version' => 3,
    'contact_id' => $entity->id,
    'return' => 'uf_id',
    'domain_id' => $domain_id,
  );
  $contact = civicrm_api('uf_match', 'getsingle', $params);
  if (empty($contact['is_error'])) {
    return array(
      'civicrm_user' => user_load($contact['uf_id']),
    );
  }
}

/**
 * replace special characters and remove non alphanumerics
 */
function _civicrm_entity_clean_login_name($str) {
  $a = array(
    'À',
    'Á',
    'Â',
    'Ã',
    'Ä',
    'Å',
    'Æ',
    'Ç',
    'È',
    'É',
    'Ê',
    'Ë',
    'Ì',
    'Í',
    'Î',
    'Ï',
    'Ð',
    'Ñ',
    'Ò',
    'Ó',
    'Ô',
    'Õ',
    'Ö',
    'Ø',
    'Ù',
    'Ú',
    'Û',
    'Ü',
    'Ý',
    'ß',
    'à',
    'á',
    'â',
    'ã',
    'ä',
    'å',
    'æ',
    'ç',
    'è',
    'é',
    'ê',
    'ë',
    'ì',
    'í',
    'î',
    'ï',
    'ñ',
    'ò',
    'ó',
    'ô',
    'õ',
    'ö',
    'ø',
    'ù',
    'ú',
    'û',
    'ü',
    'ý',
    'ÿ',
    'Ā',
    'ā',
    'Ă',
    'ă',
    'Ą',
    'ą',
    'Ć',
    'ć',
    'Ĉ',
    'ĉ',
    'Ċ',
    'ċ',
    'Č',
    'č',
    'Ď',
    'ď',
    'Đ',
    'đ',
    'Ē',
    'ē',
    'Ĕ',
    'ĕ',
    'Ė',
    'ė',
    'Ę',
    'ę',
    'Ě',
    'ě',
    'Ĝ',
    'ĝ',
    'Ğ',
    'ğ',
    'Ġ',
    'ġ',
    'Ģ',
    'ģ',
    'Ĥ',
    'ĥ',
    'Ħ',
    'ħ',
    'Ĩ',
    'ĩ',
    'Ī',
    'ī',
    'Ĭ',
    'ĭ',
    'Į',
    'į',
    'İ',
    'ı',
    'IJ',
    'ij',
    'Ĵ',
    'ĵ',
    'Ķ',
    'ķ',
    'Ĺ',
    'ĺ',
    'Ļ',
    'ļ',
    'Ľ',
    'ľ',
    'Ŀ',
    'ŀ',
    'Ł',
    'ł',
    'Ń',
    'ń',
    'Ņ',
    'ņ',
    'Ň',
    'ň',
    'ʼn',
    'Ō',
    'ō',
    'Ŏ',
    'ŏ',
    'Ő',
    'ő',
    'Œ',
    'œ',
    'Ŕ',
    'ŕ',
    'Ŗ',
    'ŗ',
    'Ř',
    'ř',
    'Ś',
    'ś',
    'Ŝ',
    'ŝ',
    'Ş',
    'ş',
    'Š',
    'š',
    'Ţ',
    'ţ',
    'Ť',
    'ť',
    'Ŧ',
    'ŧ',
    'Ũ',
    'ũ',
    'Ū',
    'ū',
    'Ŭ',
    'ŭ',
    'Ů',
    'ů',
    'Ű',
    'ű',
    'Ų',
    'ų',
    'Ŵ',
    'ŵ',
    'Ŷ',
    'ŷ',
    'Ÿ',
    'Ź',
    'ź',
    'Ż',
    'ż',
    'Ž',
    'ž',
    'ſ',
    'ƒ',
    'Ơ',
    'ơ',
    'Ư',
    'ư',
    'Ǎ',
    'ǎ',
    'Ǐ',
    'ǐ',
    'Ǒ',
    'ǒ',
    'Ǔ',
    'ǔ',
    'Ǖ',
    'ǖ',
    'Ǘ',
    'ǘ',
    'Ǚ',
    'ǚ',
    'Ǜ',
    'ǜ',
    'Ǻ',
    'ǻ',
    'Ǽ',
    'ǽ',
    'Ǿ',
    'ǿ',
  );
  $b = array(
    'A',
    'A',
    'A',
    'A',
    'A',
    'A',
    'AE',
    'C',
    'E',
    'E',
    'E',
    'E',
    'I',
    'I',
    'I',
    'I',
    'D',
    'N',
    'O',
    'O',
    'O',
    'O',
    'O',
    'O',
    'U',
    'U',
    'U',
    'U',
    'Y',
    's',
    'a',
    'a',
    'a',
    'a',
    'a',
    'a',
    'ae',
    'c',
    'e',
    'e',
    'e',
    'e',
    'i',
    'i',
    'i',
    'i',
    'n',
    'o',
    'o',
    'o',
    'o',
    'o',
    'o',
    'u',
    'u',
    'u',
    'u',
    'y',
    'y',
    'A',
    'a',
    'A',
    'a',
    'A',
    'a',
    'C',
    'c',
    'C',
    'c',
    'C',
    'c',
    'C',
    'c',
    'D',
    'd',
    'D',
    'd',
    'E',
    'e',
    'E',
    'e',
    'E',
    'e',
    'E',
    'e',
    'E',
    'e',
    'G',
    'g',
    'G',
    'g',
    'G',
    'g',
    'G',
    'g',
    'H',
    'h',
    'H',
    'h',
    'I',
    'i',
    'I',
    'i',
    'I',
    'i',
    'I',
    'i',
    'I',
    'i',
    'IJ',
    'ij',
    'J',
    'j',
    'K',
    'k',
    'L',
    'l',
    'L',
    'l',
    'L',
    'l',
    'L',
    'l',
    'l',
    'l',
    'N',
    'n',
    'N',
    'n',
    'N',
    'n',
    'n',
    'O',
    'o',
    'O',
    'o',
    'O',
    'o',
    'OE',
    'oe',
    'R',
    'r',
    'R',
    'r',
    'R',
    'r',
    'S',
    's',
    'S',
    's',
    'S',
    's',
    'S',
    's',
    'T',
    't',
    'T',
    't',
    'T',
    't',
    'U',
    'u',
    'U',
    'u',
    'U',
    'u',
    'U',
    'u',
    'U',
    'u',
    'U',
    'u',
    'W',
    'w',
    'Y',
    'y',
    'Y',
    'Z',
    'z',
    'Z',
    'z',
    'Z',
    'z',
    's',
    'f',
    'O',
    'o',
    'U',
    'u',
    'A',
    'a',
    'I',
    'i',
    'O',
    'o',
    'U',
    'u',
    'U',
    'u',
    'U',
    'u',
    'U',
    'u',
    'U',
    'u',
    'A',
    'a',
    'AE',
    'ae',
    'O',
    'o',
  );
  return strtolower(trim(preg_replace("/[^-A-Za-z0-9._]/", '_', truncate_utf8(str_replace($a, $b, $str), 60))));
}

/**
 * Given a contact object, load or create then return a drupal user.
 *
 * @param object $contact
 *   CiviCRM Contact Object
 *
 * @param $is_active
 * @param bool $notify
 * @param bool $signin
 *
 * @throws Exception
 * @return object
 *   $user Drupal user object or FALSE.
 */
function civicrm_entity_action_create_user($contact, $is_active, $notify = FALSE, $signin = FALSE, $username_format = 'first last') {
  if (!is_array($contact)) {

    // Perhaps we should be accepting object rather than array here?
    $contact = (array) $contact;
  }

  // We'll use the civicrm sync mechanism to see if Civi can match the
  // contact to an existing user.
  //
  // Don't think this is a great approach but will use for now - could
  // just create the user but no great support for that yet.
  if (empty($contact['display_name']) || empty($contact['email']) || empty($contact['first_name']) || empty($contact['last_name'])) {
    $contact = civicrm_api('contact', 'getsingle', array(
      'version' => 3,
      'id' => $contact['id'],
      'sequential' => 1,
      'return' => 'email,display_name,first_name,last_name',
    ));
  }
  if (!is_string($contact['email']) && isset($contact['email'][0]->email)) {
    $contact['email'] = $contact['email'][0]->email;
  }

  // @TODO What happens if they don't have an email at this point?
  // An email is a pre-requisite for a Drupal account, so the action
  // fails if they don't have an email.
  if (filter_var($contact['email'], FILTER_VALIDATE_EMAIL) === FALSE) {
    drupal_set_message(filter_xss("Cannot create user, email: " . $contact['email'] . " invalid!!"), 'error');
    watchdog('civicrm_entity', 'Cannot create user, email %mail invalid.', array(
      '%mail' => $contact['email'],
    ), WATCHDOG_ERROR);
    return FALSE;
  }
  $params = array(
    'name' => $contact['display_name'],
    'mail' => $contact['email'],
    'email' => $contact['email'],
    'init' => $contact['email'],
  );
  switch ($username_format) {
    case 'first last':
      $params['name'] = _civicrm_entity_clean_login_name(trim($contact['first_name']) . " " . trim($contact['last_name']));
      break;
    case 'firstlast':
      $params['name'] = _civicrm_entity_clean_login_name(trim($contact['first_name']) . " " . trim($contact['last_name']));
      break;
    case 'first.last':
      $params['name'] = _civicrm_entity_clean_login_name(trim($contact['first_name']) . "." . trim($contact['last_name']));
      break;
    case 'f.last':
      $params['name'] = _civicrm_entity_clean_login_name(substr(trim($contact['first_name']), 0, 1) . "." . trim($contact['last_name']));
      break;
    case 'first.middle.last':
      if (!empty($contact['middle_name'])) {
        $params['name'] = _civicrm_entity_clean_login_name(trim($contact['first_name']) . "." . trim($contact['middle_name']) . "." . trim($contact['last_name']));
      }
      else {
        $params['name'] = _civicrm_entity_clean_login_name(trim($contact['first_name']) . "." . trim($contact['last_name']));
      }
      break;
    case 'email':
      $params['name'] = $params['email'];
      break;
    case 'display_name':
      $params['name'] = _civicrm_entity_clean_login_name(trim($contact['display_name']));
      break;
    default:
      drupal_set_message(filter_xss("username_format " . $username_format . " unknown!!"), 'info');
      watchdog('civicrm_entity', 'username_format  %format unknown.', array(
        '%format' => $username_format,
      ), WATCHDOG_INFO);
  }
  if (empty($contact['display_name'])) {
    $contact['display_name'] = $contact['email'];
  }

  // Check if the requested username is available.
  $errors = array();
  $config = CRM_Core_Config::singleton();

  // If username already exists, try appending a numnber
  for ($ind = 1; $ind <= 10; $ind++) {
    $config->userSystem
      ->checkUserNameEmailExists($params, $errors);
    if (!empty($errors)) {
      foreach ($errors as $error) {

        //drupal_set_message(check_plain($error), 'error');
        watchdog('civicrm_entity', 'Check name %error.', array(
          '%error' => $error,
        ), WATCHDOG_ERROR);
      }
      $params['name'] = $params['name'] . "{$ind}";
    }
    else {
      $ind = 0;
      break;
    }
  }

  // There was errors in the last loop
  if ($ind != 0) {
    return FALSE;
  }
  $params['cms_name'] = $user['name'] = $params['name'];
  $params['status'] = $is_active;
  if ($notify) {
    $params['notify'] = TRUE;
  }
  $params['roles'] = array(
    DRUPAL_AUTHENTICATED_RID => 'authenticated user',
  );

  // Set $config->inCiviCRM = TRUE to prevent creating a duplicate
  // contact from user_save().
  $config = CRM_Core_Config::singleton();
  $config->inCiviCRM = TRUE;
  $user_object = user_save('', $params);
  $config->inCiviCRM = FALSE;

  // If selected in action configuration, notify the newly created
  // user & send registration link.
  if ($notify) {
    _user_mail_notify('register_no_approval_required', $user_object);
  }

  // CiviCRM doesn't do this when created off CiviCRM Form.
  //
  // Note that we 'pretend' to be logging in to make it do a ufmatch
  // on just the email.
  // Try to match Individuals, failing that, anything
  if (!empty($contact['contact_type']) && $contact['contact_type'] == 'Individual') {
    CRM_Core_BAO_UFMatch::synchronizeUFMatch($user_object, $user_object->uid, $contact['email'], 'drupal', NULL, 'Individual', TRUE);
  }
  else {
    CRM_Core_BAO_UFMatch::synchronizeUFMatch($user_object, $user_object->uid, $contact['email'], 'drupal', NULL, NULL, TRUE);
  }
  drupal_set_message('User with username, ' . $params['cms_name'] . ', has been created.');

  // If selected in action configuration, automatically sign in the
  // current user.
  if ($signin) {
    global $user;
    $user = user_load($user_object->uid);
    watchdog('civicrm_entity', 'User %name logged in via CiviCRM Entity rule execution.', array(
      '%name' => $user->name,
    ), WATCHDOG_INFO);
    $form_values = array(
      'uid' => $user->uid,
    );
    user_login_finalize($form_values);
  }
  return array(
    'civicrm_user' => $user_object,
  );
}

/**
 * Callback for username format options list, Create Linked Drupal User Account Rules action
 */

// function _civicrm_entity_options_username_format($field, $instance, $entity_type, $entity) {
function _civicrm_entity_options_username_format($field, $instance) {
  $options = array(
    'display_name' => t('Display Name'),
    'first last' => t('First Last'),
    'firstlast' => t('FirstLast'),
    'first.last' => t('First.Last'),
    'f.last' => t('F.Last'),
    'first.middle.last' => t('First.Middle.Last'),
    'email' => 'Use E-mail Address',
  );
  return $options;
}

/**
 * Implements hook_query().
 *
 * @param $type
 * @param $property
 * @param $value
 * @param $limit
 * @return array
 */
function civicrm_entity_query($type, $property, $value, $limit) {
  $return = entity_load($type, FALSE, array(
    $property => $value,
    'options' => array(
      'limit' => $limit,
    ),
  ));
  return array(
    'entity_fetched' => array_values($return),
  );
}

/**
 * Info alteration callback for the entity query action.
 * @todo this is copy of rules_action_entity_query_info_alter
 *
 * @param $element_info
 * @param RulesAbstractPlugin $element
 */
function civicrm_entity_query_info_alter(&$element_info, RulesAbstractPlugin $element) {
  $element->settings += array(
    'type' => NULL,
    'property' => NULL,
  );
  if ($element->settings['type']) {
    $element_info['parameter']['property']['options list'] = 'rules_action_entity_query_property_options_list';
    if ($element->settings['property']) {
      $wrapper = rules_get_entity_metadata_wrapper_all_properties($element);
      if (isset($wrapper->{$element->settings['property']}) && ($property = $wrapper->{$element->settings['property']})) {
        $element_info['parameter']['value']['type'] = $property
          ->type();
        $element_info['parameter']['value']['options list'] = $property
          ->optionsList() ? 'rules_action_entity_query_value_options_list' : FALSE;
      }
    }
  }
  $element_info['provides']['entity_fetched']['type'] = 'list<' . $element->settings['type'] . '>';
}

/**
 * Load or create user as appropriate.
 *
 * @param $entity
 * @param int $is_active
 * @param int $notify
 *
 * @return object
 */
function civicrm_entity_action_load_create_user($entity, $is_active = 0, $notify = 0) {
  if ($user = civicrm_entity_action_load_user($entity)) {
    if ($is_active && !$user['civicrm_user']->status) {
      $user['civicrm_user']->status = $is_active;
      $user['civicrm_user']->save;
    }
    return $user;
  }
  return civicrm_entity_action_create_user((array) $entity, $is_active, $notify);
}

/**
 * @param $user
 *
 * @param null $email
 *
 * @return mixed
 */
function civicrm_entity_action_load_create_contact($user, $email = NULL) {
  try {
    return civicrm_entity_action_load_contact($user);
  } catch (CiviCRM_API3_Exception $e) {
    $ufMatch = CRM_Core_BAO_UFMatch::synchronizeUFMatch($user, $user->uid, $email ? $email : $user->mail, 'Drupal', FALSE, 'Individual');
    $entities = entity_load('civicrm_contact', array(
      $ufMatch->contact_id,
    ));
    return array(
      'civicrm_contact' => reset($entities),
    );
  }
}

/**
 * @param $user
 *
 * @return mixed
 * @throws CiviCRM_API3_Exception
 */
function civicrm_entity_action_load_contact($user) {
  if (!civicrm_initialize()) {
    return;
  }
  $contact_id = civicrm_api3('uf_match', 'getvalue', array(
    'uf_id' => $user->uid,
    'return' => 'contact_id',
    'domain_id' => CRM_Core_Config::domainID(),
  ));
  $entities = entity_load('civicrm_contact', array(
    $contact_id,
  ));
  return array(
    'civicrm_contact' => reset($entities),
  );
}

/**
 * gt location options
 * @return array
 */
function civicrm_entity_get_locations() {
  if (!civicrm_initialize()) {
    return array();
  }
  $locations = civicrm_api3('address', 'getoptions', array(
    'field' => 'location_type_id',
  ));
  $locations = $locations['values'];
  return array(
    '0' => 'Primary',
  ) + $locations;
}

/**
 * @param $contact
 * @param $location_type_id
 *
 * @return array
 */
function civicrm_entity_contact_has_location_element_email($contact, $location_type_id) {
  if (!empty($contact->email)) {
    return TRUE;
  }
  $email = civicrm_entity_contact_get_location_element('email', $contact, $location_type_id);
  if (!empty($email['fetched_email'])) {
    return TRUE;
  }
  return FALSE;
}

/**
 * @param $contact
 * @param $location_type_id
 *
 * @return array
 */
function civicrm_entity_contact_get_email($contact, $location_type_id) {
  return civicrm_entity_contact_get_location_element('email', $contact, $location_type_id);
}

/**
 * @param $contact
 * @param $location_type_id
 *
 * @return array|bool
 */
function civicrm_entity_contact_get_location_element($entity, &$contact, $location_type_id) {
  $params = array(
    'contact_id' => $contact->id,
    'sequential' => 1,
    'on_hold' => 0,
  );
  if ($location_type_id) {
    $params['location_type_id'] = $location_type_id;
  }
  else {
    $params['is_primary'] = 1;
  }
  $result = civicrm_api3($entity, 'get', $params);
  if (!$result['count']) {
    return array(
      'civicrm_' . $entity => '',
    );
  }
  return array(
    'fetched_' . $entity => $result['values'][0][$entity],
  );
}

/**
 * @param $contact
 * @param $subject
 * @param $message_text
 * @param $message_html
 * @param null $from
 */
function civicrm_entity_contact_send_email($contact, $subject, $message_text, $message_html, $from = NULL) {
  $email = civicrm_api3('email', 'get', array(
    'contact_id' => $contact->id,
    'is_primary' => 1,
    'on_hold' => 0,
    'sequential' => 1,
  ));
  if (!$email['count']) {
    watchdog('civicrm_entity_rules', 'no email could be sent to !contact_id', array(
      '!contact_id' => $contact->id,
    ));
  }
  $params = array();
  $params['from'] = !empty($from) ? str_replace(array(
    "\r",
    "\n",
  ), '', $from) : 'Admin';
  $params['toEmail'] = $email['values'][0]['email'];
  $params['subject'] = $subject;
  $params['text'] = $message_text;
  $params['html'] = $message_html;
  CRM_Utils_Mail::send($params);
  civicrm_api3('activity', 'create', array(
    'activity_type_id' => 'Email',
    'source_contact_id' => $contact->id,
    'target_contact_id' => $contact->id,
    'subject' => $subject,
    'details' => $message_html,
  ));
}

/**
 * Callback for Unassign CiviCRM Contact from Group Rules action
 *
 * @param $contact
 * @param $group_ids
 */
function civicrm_entity_rules_action_assign_contact_to_group($contact, $group_ids) {
  if (!civicrm_initialize()) {
    return;
  }
  try {
    foreach ($group_ids as $key => $group) {
      $contact_id = $contact->id
        ->value();
      $params = array(
        'sequential' => 1,
        'contact_id' => $contact_id,
        'group_id' => $group,
      );
      $get_result = civicrm_api3('GroupContact', 'Get', $params);
      if (!$get_result['count']) {
        $params = array(
          'contact_id' => $contact_id,
          'group_id' => $group,
        );
        $create_result = civicrm_api3('GroupContact', 'Create', $params);
      }
    }
  } catch (CiviCRM_API3_Exception $e) {
    watchdog('civicrm_entity', t('Error assigning contact to groups via Assign CiviCRM Contact to Group rule'));
  }
}

/**
 * Callback for Assign CiviCRM Contact to Group Rules action
 *
 * @param $contact
 * @param $group_ids
 */
function civicrm_entity_rules_action_unassign_contact_to_group($contact, $group_ids) {
  if (!civicrm_initialize()) {
    return;
  }
  try {
    foreach ($group_ids as $key => $group) {
      $contact_id = $contact->id
        ->value();
      $params = array(
        'contact_id' => $contact_id,
        'group_id' => $group,
      );
      $get_result = civicrm_api3('GroupContact', 'Get', $params);
      if ($get_result['count']) {
        $delete_result = civicrm_api3('GroupContact', 'delete', $params);
      }
    }
  } catch (CiviCRM_API3_Exception $e) {
    watchdog('civicrm_entity', t('Error unassigning contact to groups via Assign CiviCRM Contact from Group rule'));
  }
}

/**
 * Callback for Assign CiviCRM Contact to Group Rules action, Group selection options list
 * also used by the Contact is in Group condition
 *
 * @return array
 */
function civicrm_entity_rules_assign_contact_group_options_list() {
  if (!civicrm_initialize()) {
    return [];
  }
  $result = civicrm_api3('Group', 'get', [
    'return' => [
      "title",
    ],
    'is_active' => 1,
    'options' => [
      'limit' => 0,
      'sort' => "title ASC",
    ],
  ]);
  $options = [];
  if (!$result['is_error'] && !empty($result['values'])) {
    foreach ($result['values'] as $key => $value) {
      $options[$key] = $value['title'];
    }
  }
  return $options;
}

/**
 * Callback for Contact is of subtype condition, Contact Sub Type options list
 *
 * @return array
 */
function civicrm_entity_rules_contact_subtype_options_list() {
  if (!civicrm_initialize()) {
    return;
  }
  try {
    $result = civicrm_api3('ContactType', 'get', array(
      'sequential' => 1,
      'parent_id' => array(
        'IS NOT NULL' => 1,
      ),
    ));
    if (!empty($result['count'])) {
      $options = array();
      foreach ($result['values'] as $index => $subtype) {
        $options[$subtype['name']] = $subtype['label'];
      }
      return $options;
    }
  } catch (CiviCRM_API3_Exception $e) {
    return;
  }
}

/**
 * Implement the pre hook and delete Drupal field data when civicrm deletes an entity.
 *
 * @param $op
 * @param $object_name
 * @param $id
 * @param $params
 */
function civicrm_entity_civicrm_pre($op, $objectName, $id, &$params) {
  if ($op == 'delete') {
    $entity_type = '';
    $valid_entities = _civicrm_entity_enabled_entities();
    switch ($objectName) {
      case 'Individual':
      case 'Household':
      case 'Organization':
        $entity_type = 'civicrm_contact';
        break;
      default:
        $entity_type = 'civicrm_' . _civicrm_entity_get_entity_name_from_camel($objectName);
        break;
    }
    if (isset($valid_entities[$entity_type])) {
      if ($objectName == 'EntityTag') {
        $api_params = [
          'sequential' => 1,
          'entity_id' => $params[0][0],
          'entity_table' => $params[1],
        ];
        $api_results = civicrm_api3('EntityTag', 'get', $api_params);
        if (!empty($api_results['values'])) {
          foreach ($api_results as $delta => $result) {
            if ($result['tag_id'] == $id) {
              $entity = entity_load_single($entity_type, $result['id']);
              break;
            }
          }
        }
      }
      else {
        $entity = entity_load_single($entity_type, $id);
      }
      if (!empty($entity)) {
        field_attach_delete($entity_type, $entity);
      }
    }
  }
}

/**
 * Implement the post hook and fire the corresponding rules event.
 *
 * @param $op
 * @param $object_name
 * @param $object_id
 * @param $object_ref
 */
function civicrm_entity_civicrm_post($op, $object_name, $object_id, &$object_ref) {
  if (!module_exists('rules')) {
    return;
  }
  $contact_types = array(
    'Individual',
    'Household',
    'Organization',
  );
  if (in_array($object_name, $contact_types)) {
    $object_name = 'Contact';
  }
  $valid_objects = _civicrm_entity_enabled_entities();
  $entity_name = _civicrm_entity_get_entity_name_from_camel($object_name);
  if (!in_array($entity_name, $valid_objects, TRUE)) {
    return;
  }
  $event_name = NULL;
  switch ($op) {
    case 'create':
    case 'edit':
      $event_name = 'civicrm_' . $entity_name . "_{$op}";
      break;

    /*
     *  Here we are checking if the contact entity is being deleted
     *  AND if the contact has sent to the trash already. If the
     *  contact has been sent to the trash, we want to permanently
     *  delete it. If not, we will just fire the "update" event to
     *  let the subscribed rules know that the "is_delete" flag has
     *  changed.
     */
    case 'delete':
      if ($entity_name == 'contact') {
        if ($object_ref->is_deleted == 1) {
          $event_name = 'civicrm_' . $entity_name . "_{$op}";
        }
        else {

          //we are receiving a reference to the contact object before the flag has changed. So we need to force it to change.
          $object_ref->is_deleted = 1;
          $event_name = 'civicrm_' . $entity_name . "_edit";
        }
      }
      else {
        $event_name = 'civicrm_' . $entity_name . "_{$op}";
      }
      break;

    //We need to alert subscribed rules that the "is_delete" flag has changed.
    case 'restore':

      //we are receiving a reference to the contact object before the flag has changed. So we need to force it to change.
      $object_ref->is_deleted = 0;
      $event_name = 'civicrm_' . $entity_name . "_edit";
      break;
    default:
      break;
  }
  if ($entity_name == 'entity_tag') {

    // Argh entity tag is completely non-standard!!!
    // @see CRM-11933
    foreach ($object_ref[0] as $entity_tag) {
      $object = new CRM_Core_BAO_EntityTag();
      $object->entity_id = $entity_tag;
      $object->entity_table = 'civicrm_contact';
      $object->tag_id = $object_id;
      if ($object
        ->find(TRUE)) {

        // This find is probably not necessary but until more testing
        // on the tag create is done I will.
        rules_invoke_event($event_name, $object);
      }
    }
  }
  else {
    if ($event_name) {

      //this addition here causes a custom field value to be saved every time, duplicates and empty values can be made

      /*if(isset($object_ref->id)){
          $entity = entity_load_single('civicrm_' . $entity_name, $object_ref->id);
        }*/
      rules_invoke_event($event_name, $object_ref);

      //rules_invoke_event($event_name, $entity);
    }
  }
}

/**
 * Convert possibly camel name to underscore separated entity name.
 *
 * @see _civicrm_api_get_entity_name_from_camel()
 *
 * @TODO Why don't we just call the above function directly?
 * Because the function is officially 'likely' to change as it is an internal api function and calling api functions directly is explicitly not supported
 *
 * @param string $entity
 *   Entity name in various formats e.g:
 *     Contribution => contribution,
 *     OptionValue => option_value,
 *     UFJoin => uf_join.
 *
 * @return string
 *   $entity entity name in underscore separated format
 */
function _civicrm_entity_get_entity_name_from_camel($entity) {
  if ($entity == strtolower($entity)) {
    return $entity;
  }
  else {
    $entity = ltrim(strtolower(str_replace('U_F', 'uf', preg_replace('/(?=[A-Z])/', '_$0', $entity))), '_');
  }
  return $entity;
}

/**
 * Load contact entity according to user id.
 *
 * @param $data
 * @param array $options
 * @param $name
 * @param $type
 * @param $info
 *
 * @return null
 */
function civicrm_entity_user_contact_get($data, array $options, $name, $type, $info) {
  if (!module_exists('civicrm') || !function_exists('civicrm_initialize')) {
    return;
  }
  if (!civicrm_initialize()) {
    return;
  }
  $domain_id = civicrm_api('domain', 'getvalue', array(
    'version' => 3,
    'return' => 'id',
    'current_domain' => TRUE,
  ));
  $contact = civicrm_api('uf_match', 'getsingle', array(
    'version' => 3,
    'return' => 'contact_id',
    'uf_id' => $data->uid,
    'domain_id' => $domain_id,
  ));
  if (!empty($contact['contact_id'])) {
    $entity = entity_load('civicrm_contact', array(
      $contact['contact_id'],
    ));
    return $entity[$contact['contact_id']];
  }
  else {
    return NULL;
  }
}

/**
 * * Load user entity according to contact id.
 *
 * @param $data
 * @param array $options
 * @param $name
 * @param $type
 * @param $info
 * @return int|null
 */
function civicrm_entity_contact_user_get($data, array $options, $name, $type, $info) {
  if (!module_exists('civicrm') || !function_exists('civicrm_initialize')) {
    return;
  }
  if (!civicrm_initialize()) {
    return;
  }
  $domain_id = civicrm_api('domain', 'getvalue', array(
    'version' => 3,
    'return' => 'id',
    'current_domain' => TRUE,
  ));
  $uf_info = civicrm_api('uf_match', 'getsingle', array(
    'version' => 3,
    'return' => 'uf_id',
    'contact_id' => $data->contact_id,
    'domain_id' => $domain_id,
  ));
  if (!empty($uf_info['uf_id'])) {
    $entity = entity_load('user', array(
      $uf_info['uf_id'],
    ));
    return $entity[$uf_info['uf_id']];
  }
  else {
    return NULL;
  }
}

/**
 * Set user entity according to contact id.
 *
 * @param $data
 * @param $name
 * @param $value
 * @param $langcode
 * @param $type
 * @param $info
 * @return null|void
 */
function civicrm_entity_contact_user_set(&$data, $name, $value, $langcode, $type, $info) {
  if (!is_null($data->id)) {
    if ($user = user_load($value)) {
      if (!module_exists('civicrm') || !function_exists('civicrm_initialize')) {
        return;
      }
      if (!civicrm_initialize()) {
        return;
      }
      $domain_id = civicrm_api('domain', 'getvalue', array(
        'version' => 3,
        'return' => 'id',
        'current_domain' => TRUE,
      ));
      $uf_info = civicrm_api('uf_match', 'create', array(
        'version' => 3,
        'contact_id' => $data->id,
        'uf_id' => $user->uid,
        'uf_name' => $user->name,
        'domain_id' => $domain_id,
      ));
      if (!$uf_info['is_error']) {
        $data->civi_user = $user;
      }
      else {
        return NULL;
      }
    }
  }
}

/**
 * Implements hook_rules_action_info_alter().
 *
 * I can't seem to get my info_alter function called by hook so am doing this hacky intercept
 * to call my function (& then go back to the main function if not a CiviCRM entity
 *
 * @param $info
 */
function civicrm_entity_rules_action_info_alter(&$info) {
  $info['entity_create']['callbacks']['info_alter'] = 'civicrm_entity_rules_action_entity_create_info_alter';
}

/**
 * Info alteration callback for the entity create action.
 *
 * Here we add a tonne of fields to civicrm entity create
 *
 * @param $element_info
 * @param RulesAbstractPlugin $element
 */
function civicrm_entity_rules_action_entity_create_info_alter(&$element_info, RulesAbstractPlugin $element) {
  if (empty($element->settings['type']) || substr($element->settings['type'], 0, 8) != 'civicrm_') {
    module_load_include('inc', 'rules', 'modules/entity.eval');
    return rules_action_entity_create_info_alter($element_info, $element);
  }
  module_load_include('inc', 'rules', 'modules/data.rules');
  $civicrm_entity = str_replace('civicrm_', '', $element->settings['type']);
  if (!empty($element->settings['type']) && entity_get_info($element->settings['type'])) {
    $wrapper = entity_metadata_wrapper($element->settings['type']);

    // Add the data type's needed parameter for loading to the parameter info.
    $priority_fields = array(
      'type',
    );
    foreach ($wrapper as $name => $child) {
      $info = $child
        ->info();
      if (substr($info['type'], 0, 8) == 'civicrm_') {

        //we are already showing id lets not show entity too
        continue;
      }
      $info = array_merge(array(
        'type' => 'text',
      ), $info);

      // Prefix parameter names to avoid name clashes with existing parameters.
      $element_info['parameter']['param_' . $name] = array_intersect_key($info, array_flip(array(
        'type',
        'label',
        'description',
      )));
      $element_info['parameter']['param_' . $name]['options list'] = $child
        ->optionsList() ? 'rules_action_entity_parameter_options_list' : FALSE;
      $element_info['parameter']['param_' . $name]['optional'] = $element_info['parameter']['param_' . $name]['allow null'] = empty($info['required']);
      $element_info['parameter']['param_' . $name]['default mode'] = 'selector';
      if (!empty($info['required'])) {
        $priority_fields[] = 'param_' . $name;
      }
    }
    unset($element_info['parameter']['param_type']);
    if (in_array($civicrm_entity, array(
      'participant',
      'membership',
    ))) {
      $element_info['parameter']['param_civicrm_contribution'] = array(
        'type' => 'civicrm_contribution',
        'label' => 'CiviCRM Contribution',
        'description' => t('Optional CiviCRM contribution for paid ' . $civicrm_entity),
        'optional' => TRUE,
        'allow null' => TRUE,
        'default mode' => 'selector',
      );
      $priority_fields[] = 'param_civicrm_contribution';
    }
    $element_info['parameter'] = array_merge(array_flip($priority_fields), $element_info['parameter']);
    foreach (civicrm_entity_get_custom_fields($civicrm_entity) as $fieldname => $field) {
      $element_info['parameter'][$fieldname] = array(
        'type' => $field['type'],
        'label' => $field['label'],
        'optional' => TRUE,
        'default mode' => 'selector',
        'allow null' => TRUE,
      );
    }
    $element_info['provides']['entity_created']['type'] = $element->settings['type'];
    if (($bundleKey = $wrapper
      ->entityKey('bundle')) && isset($element->settings['param_' . $bundleKey])) {
      $element_info['provides']['entity_created']['bundle'] = $element->settings['param_' . $bundleKey];
    }
  }
}

/**
 * Gets the custom fields for a CiviCRM entity
 *
 * @param $entity
 * @return array
 */
function civicrm_entity_get_custom_fields($entity, $types = array(
  'Integer' => 'integer',
  'String' => 'text',
  'Date' => 'date',
)) {
  if (!civicrm_initialize()) {
    return array();
  }
  $fields = civicrm_api3($entity, 'getfields', array(
    'action' => 'create',
    'getoptions' => TRUE,
  ));
  $fields = $fields['values'];
  foreach ($fields as $field_name => $field) {
    if (substr($field_name, 0, 7) != 'custom_' || !in_array($field['data_type'], array_keys($types))) {
      unset($fields[$field_name]);
    }
    else {
      $fields[$field_name]['type'] = $types[$field['data_type']];
    }
  }
  return $fields;
}

/**
 * Loads UI controller and generates view pages for civicrm entities
 *
 * @param $entity
 * @param string $entity_type
 *
 * @return string content
 */
function civicrm_entity_page_view($entity, $entity_type) {
  $content = "";
  if (!empty($entity->id)) {
    $id = $entity->id;
  }
  elseif (!is_object($entity) && is_numeric($entity)) {
    $id = $entity;
  }
  if (!empty($entity) && is_object($entity)) {
    if ($entity_type == 'civicrm_contact') {

      //for some reason the is_deleted column of the contact record is coming to the entity

      // as contact_is_deleted ...special handling to have the page value set properly
      $entity->is_deleted = $entity->contact_is_deleted;
    }
    $content_array = entity_view($entity_type, array(
      $entity->id => $entity,
    ));
    $content = drupal_render($content_array);
    $label = entity_label($entity_type, $entity);
  }
  else {
    $content = '<p>No ' . $entity_type . ' record found for id: ' . $id . '</p>';
  }
  if (!empty($label) && ($entity_type == 'civicrm_event' || $entity_type == 'civicrm_contact' || $entity_type == 'civicrm_group')) {
    drupal_set_title($label);
  }
  else {
    $formatted_entity_title = ucwords(str_replace('_', ' ', $entity_type));
    drupal_set_title($formatted_entity_title . ' ' . $id);
  }
  return $content;
}

/**
 * Implements hook_ds_field_settings_form().
 */
function civicrm_entity_ds_field_settings_form($field) {
  return ds_ds_field_settings_form($field);
}

/**
 * Implements hook_ds_field_format_summary().
 */
function civicrm_entity_ds_field_format_summary($field) {
  return ds_ds_field_format_summary($field);
}

/**
 * Implementes hook_form_alter().
 */
function civicrm_entity_form_alter(&$form, &$form_state, $form_id) {

  // For the manage display form, add a submit handler to make display suite custom civicrm "field" settings stick
  if ($form_id == 'field_ui_display_overview_form') {
    if (!civicrm_initialize(TRUE)) {
      return;
    }
    $entities = _civicrm_entity_enabled_entities();
    $entity_type = $form['#entity_type'];
    if (array_key_exists($entity_type, $entities)) {
      $submits = array();
      $submits[] = '_civicrm_entity_manage_display_submit';
      foreach ($form['#submit'] as $submit_callback) {
        $submits[] = $submit_callback;
      }
      $form['#submit'] = $submits;
      if ($entity_type == 'civicrm_contact' && !empty($form['fields']['civi_user'])) {
        $form['fields']['civi_user']['human_name']['#markup'] = 'Drupal User (broken, do not display';
      }
    }
  }

  // for the manage fields form, if display suite forms is enabled, add a validation handler
  // that enforces that the entity API required fields are set to be on the form
  if ($form_id == 'field_ui_field_overview_form' && module_exists('ds_forms') && !empty($form['#ds_layout'])) {
    $entities = _civicrm_entity_enabled_entities();
    $entity_type = $form['#entity_type'];
    if (array_key_exists($entity_type, $entities)) {
      $entity_props = entity_get_property_info($entity_type);
      foreach ($entity_props['properties'] as $property => $info) {
        if (!empty($info['required'])) {
          $form['fields'][$property]['label']['#markup'] = '<span class="form-required" title="This field is required.">* </span>' . '<span title="This field is required.">' . $form['fields'][$property]['label']['#markup'] . '</span>';
        }
      }
      $form['#validate'][] = '_civicrm_entity_manage_display_suite_forms_validate';
    }
  }
}

/**
 * Custom submit handler
 *
 * Updates bundle visibility settings if a display suite layout is used
 *
 * @see civicrm_entity_form_alter().
 */
function _civicrm_entity_manage_display_submit(&$form, &$form_state) {
  if (isset($form_state['values']['additional_settings']['layout']) && $form_state['values']['additional_settings']['layout'] != '') {
    $entity_type = $form['#entity_type'];
    $bundle = $form['#bundle'];
    $bundle_settings = field_bundle_settings($entity_type, $bundle);
    foreach ($bundle_settings['extra_fields']['display'] as $key => $field) {
      $form_state['values']['fields'][$key]['type'] = 'visible';
    }
  }
}

/**
 * Custom validation handler
 *
 * Enforces that required entity API fields are set to display on form
 *
 * @param $form
 * @param $form_state
 */
function _civicrm_entity_manage_display_suite_forms_validate(&$form, &$form_state) {
  $entity_type = $form['#entity_type'];
  $entity_props = entity_get_property_info($entity_type);
  foreach ($entity_props['properties'] as $property => $info) {
    if (!empty($info['required']) && (empty($form_state['values']['fields'][$property]['region']) || $form_state['values']['fields'][$property]['region'] == 'hidden')) {
      form_set_error('fields][' . $property, $info['label'] . ' property is required by CiviCRM API to be included on this form.');
    }
  }
}

/**
 * Implements hook_ds_fields_info().
 *
 * Setup custom display suite "field" handling for CiviCRM properties
 *
 * @param $entity_type
 * @return array|void
 */
function civicrm_entity_ds_fields_info($entity_type) {
  $fields = array();
  if (!civicrm_initialize(TRUE)) {
    return;
  }
  $enabled_entities = _civicrm_entity_enabled_entities();
  if (empty($enabled_entities[$entity_type])) {
    return;
  }
  $properties = _civicrm_entity_getproperties(substr($entity_type, 8), 'property_info');
  $civicrm_entity_info = civicrm_entity_get_supported_entity_info($entity_type);

  // Basic wrapper and class settings for all CiviCRM properties, also makes the label visibility work
  foreach ($properties as $name => $settings) {
    $fields[$entity_type][$name] = array(
      'title' => $settings['label'],
      'field_type' => 2,
      'function' => '_civicrm_entity_render_fields',
      'file' => drupal_get_path('module', 'civicrm_entity') . '/civicrm_entity.ds.inc',
      'properties' => array(
        'settings' => array(
          'wrapper' => array(
            'type' => 'textfield',
            'description' => t('Eg: h1, h2, p'),
          ),
          'class' => array(
            'type' => 'textfield',
            'description' => t('Put a class on the wrapper. Eg: block-title'),
          ),
        ),
        'default' => array(
          'wrapper' => 'div',
          'class' => 'civicrm-field',
        ),
      ),
    );

    // could do some date formatter logic all at one here

    /* if(isset($settings['mysql_type') && $settings['mysql_type'] == 'datetime') {
          $fields[$entity_type][$name]['date']['date'] = TRUE;
          $fields[$entity_type][$name]['date']['granularity'] = $settings['granularity'];
       }*/
  }
  if (isset($civicrm_entity_info['display suite'])) {
    if (isset($civicrm_entity_info['display suite']['link fields']) && count($civicrm_entity_info['display suite']['link fields'])) {
      _civicrm_entity_link_addformatters($entity_type, $civicrm_entity_info['display suite']['link fields'], $fields);
    }
    if (isset($civicrm_entity_info['display suite']['option fields']) && count($civicrm_entity_info['display suite']['option fields'])) {
      _civicrm_entity_option_addformatters($entity_type, $civicrm_entity_info['display suite']['option fields'], $fields);
    }
    if (isset($civicrm_entity_info['display suite']['boolean fields']) && count($civicrm_entity_info['display suite']['boolean fields'])) {
      _civicrm_entity_yesno_addformatters($entity_type, $civicrm_entity_info['display suite']['boolean fields'], $fields);
    }
    if (isset($civicrm_entity_info['display suite']['date fields']) && count($civicrm_entity_info['display suite']['date fields'])) {
      _civicrm_entity_date_addformatters($entity_type, $civicrm_entity_info['display suite']['date fields'], $fields);
    }
    if (isset($civicrm_entity_info['display suite']['timestamp fields']) && count($civicrm_entity_info['display suite']['timestamp fields'])) {
      _civicrm_entity_timestamp_addformatters($entity_type, $civicrm_entity_info['display suite']['timestamp fields'], $fields);
    }
  }
  if (isset($fields[$entity_type])) {
    return array(
      $entity_type => $fields[$entity_type],
    );
  }
}

/**
 * If Rules is available, use that module's redirect on shutdown.
 */
function civicrm_entity_drupal_goto($path = '', array $options = array(), $http_response_code = 302) {
  if (module_exists('rules')) {
    rules_action_drupal_goto($path);
  }
  else {
    drupal_goto($path, $options, $http_response_code);
  }
}

Functions

Namesort descending Description
civicrm_entity_access Entity access callback.
civicrm_entity_action_create_user Given a contact object, load or create then return a drupal user.
civicrm_entity_action_load_contact
civicrm_entity_action_load_create_contact
civicrm_entity_action_load_create_user Load or create user as appropriate.
civicrm_entity_action_load_user Given a contact object return the Drupal user.
civicrm_entity_autocomplete Menu callback for autocomplete search function
civicrm_entity_civicrm_contact_label_callback Label callback for civicrm_contact entity type.
civicrm_entity_civicrm_post Implement the post hook and fire the corresponding rules event.
civicrm_entity_civicrm_pre Implement the pre hook and delete Drupal field data when civicrm deletes an entity.
civicrm_entity_contact_get_email
civicrm_entity_contact_get_location_element
civicrm_entity_contact_has_location_element_email
civicrm_entity_contact_is_in_group Condition Contact is in Group
civicrm_entity_contact_is_subtype Condition Contact is of Sub type
civicrm_entity_contact_send_email
civicrm_entity_contact_user_get Load user entity according to contact id.
civicrm_entity_contact_user_set Set user entity according to contact id.
civicrm_entity_drupal_goto If Rules is available, use that module's redirect on shutdown.
civicrm_entity_ds_fields_info Implements hook_ds_fields_info().
civicrm_entity_ds_field_format_summary Implements hook_ds_field_format_summary().
civicrm_entity_ds_field_settings_form Implements hook_ds_field_settings_form().
civicrm_entity_entity_info Here we declare selected CiviCRM entities to Drupal.
civicrm_entity_entity_property_info_alter Here we declare Selected CiviCRM entities fields to Drupal.
civicrm_entity_field_extra_fields_alter Implements hook_field_extra_fields_alter().
civicrm_entity_fk_entities_to_produce_widget_for Utility function to return which entities we're allowing for using a special FK FAPI Widget for some FK Reference fields
civicrm_entity_form_alter Implementes hook_form_alter().
civicrm_entity_get_custom_fields Gets the custom fields for a CiviCRM entity
civicrm_entity_get_field_options Utility function to get the options for a civicrm options field for the widget metadata
civicrm_entity_get_field_type Please document this function.
civicrm_entity_get_field_widget Utility function that takes the provided field specs from a getfields call and builds a widget property array on the property metadata info
civicrm_entity_get_locations gt location options
civicrm_entity_get_schema Get schema for entities.
civicrm_entity_get_supported_entity_info Get supported CiviCRM entity information
civicrm_entity_loader_load CiviCRM Entity view,edit,delete form autoloader callback
civicrm_entity_menu Implements hook_menu().
civicrm_entity_menu_alter Implements hook_menu_alter().
civicrm_entity_metadata_civicrm_entity_get_properties Implement getter callback.
civicrm_entity_op_access Granular per-entity CRUD access control
civicrm_entity_page_view Loads UI controller and generates view pages for civicrm entities
civicrm_entity_permission Implements hook_permission().
civicrm_entity_query Implements hook_query().
civicrm_entity_query_info_alter Info alteration callback for the entity query action. @todo this is copy of rules_action_entity_query_info_alter
civicrm_entity_rules_action_assign_contact_to_group Callback for Unassign CiviCRM Contact from Group Rules action
civicrm_entity_rules_action_entity_create_info_alter Info alteration callback for the entity create action.
civicrm_entity_rules_action_info_alter Implements hook_rules_action_info_alter().
civicrm_entity_rules_action_unassign_contact_to_group Callback for Assign CiviCRM Contact to Group Rules action
civicrm_entity_rules_assign_contact_group_options_list Callback for Assign CiviCRM Contact to Group Rules action, Group selection options list also used by the Contact is in Group condition
civicrm_entity_rules_contact_subtype_options_list Callback for Contact is of subtype condition, Contact Sub Type options list
civicrm_entity_schema_alter Implements hook_schema_alter().
civicrm_entity_supported_entities_info Whitelist of total CiviCRM Entity related info metadata
civicrm_entity_theme Implements hook_theme().
civicrm_entity_uri_callback URI Callback for entities
civicrm_entity_user_contact_get Load contact entity according to user id.
civicrm_entity_user_creatable Condition Drupal User Account can be created for contact (creates contact).
civicrm_entity_user_exists Condition Drupal User Account exists for contact.
civicrm_entity_user_exists_or_creatable Condition Drupal User Account can be created or exists for contact.
civicrm_entity_views_data_alter Implements hook_views_data_alter().
_civicrm_enabled_entity_alter_labels
_civicrm_enabled_entity_alter_whitelist Allow extensions to alter entities
_civicrm_entity_available_entities We gather a list entities that are available, either as hardcoded in this module, or provided by the hook
_civicrm_entity_available_entities_get_option_set Build an option set array used by the configure CiviCRM Entity form, and various submodules
_civicrm_entity_chained_fks Please document this function.
_civicrm_entity_clean_login_name replace special characters and remove non alphanumerics
_civicrm_entity_db_select Utility function to do db_select for FK reference autocomplete widgets
_civicrm_entity_enabled_entities Whitelist of enabled entities. We don't have a compelling reason for not including all entities but some entities are fairly non-standard and of course the rule hook would instantiate rules more often if all were enabled.
_civicrm_entity_getproperties Calculate fields for entities
_civicrm_entity_get_entities_list Call back function used to get select list options for some FK Reference fields.
_civicrm_entity_get_entity_name_from_camel Convert possibly camel name to underscore separated entity name.
_civicrm_entity_get_title Title callback
_civicrm_entity_labels Provide label (column) for each entity types - default to id if nothing specified.
_civicrm_entity_manage_display_submit Custom submit handler
_civicrm_entity_manage_display_suite_forms_validate Custom validation handler
_civicrm_entity_options_username_format