View source
<?php
namespace Drupal\salesforce_mapping\Form;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\salesforce_mapping\MappingConstants;
use Drupal\Core\Url;
use Drupal\salesforce\Event\SalesforceEvents;
use Drupal\salesforce\Event\SalesforceErrorEvent;
abstract class SalesforceMappingFormCrudBase extends SalesforceMappingFormBase {
protected $storageController;
public function buildForm(array $form, FormStateInterface $form_state) {
if (!$this
->ensureConnection()) {
return $form;
}
$form = parent::buildForm($form, $form_state);
$mapping = $this->entity;
$form['label'] = [
'#type' => 'textfield',
'#title' => $this
->t('Label'),
'#default_value' => $mapping
->label(),
'#required' => TRUE,
'#weight' => -30,
];
$form['id'] = [
'#type' => 'machine_name',
'#required' => TRUE,
'#default_value' => $mapping
->id(),
'#maxlength' => EntityTypeInterface::ID_MAX_LENGTH,
'#machine_name' => [
'exists' => [
'Drupal\\salesforce_mapping\\Entity\\SalesforceMapping',
'load',
],
'source' => [
'label',
],
],
'#disabled' => !$mapping
->isNew(),
'#weight' => -20,
];
$form['drupal_entity'] = [
'#title' => $this
->t('Drupal entity'),
'#type' => 'details',
'#attributes' => [
'id' => 'edit-drupal-entity',
],
'#open' => $mapping
->isNew(),
];
$entity_types = $this
->getEntityTypeOptions();
$form['drupal_entity']['drupal_entity_type'] = [
'#title' => $this
->t('Drupal Entity Type'),
'#id' => 'edit-drupal-entity-type',
'#type' => 'select',
'#description' => $this
->t('Select a Drupal entity type to map to a Salesforce object.'),
'#options' => $entity_types,
'#default_value' => $mapping->drupal_entity_type,
'#required' => TRUE,
'#empty_option' => $this
->t('- Select -'),
'#ajax' => [
'callback' => [
$this,
'bundleCallback',
],
'event' => 'change',
'wrapper' => 'drupal_bundle',
],
];
$form['drupal_entity']['drupal_bundle'] = [
'#title' => $this
->t('Bundle'),
'#type' => 'select',
'#default_value' => $mapping->drupal_bundle,
'#empty_option' => $this
->t('- Select -'),
'#options' => $this
->getBundleOptions(),
'#required' => TRUE,
'#prefix' => '<div id="drupal_bundle">',
'#suffix' => '</div>',
'#states' => [
'visible' => [
':input[name="drupal_entity_type"]' => [
'!value' => '',
],
],
],
];
$input = $form_state
->getUserInput();
if (!empty($input) && !empty($input['drupal_entity_type'])) {
$entity_type = $input['drupal_entity_type'];
}
else {
$entity_type = $form['drupal_entity']['drupal_entity_type']['#default_value'];
}
$bundle_info = $this->entityManager
->getBundleInfo($entity_type);
if (!empty($bundle_info)) {
$form['drupal_entity']['drupal_bundle']['#options'] = [];
$form['drupal_entity']['drupal_bundle']['#title'] = $this
->t('@entity_type Bundle', [
'@entity_type' => $entity_types[$entity_type],
]);
foreach ($bundle_info as $key => $info) {
$form['drupal_entity']['drupal_bundle']['#options'][$key] = $info['label'];
}
}
$form['salesforce_object'] = [
'#title' => $this
->t('Salesforce object'),
'#id' => 'edit-salesforce-object',
'#type' => 'details',
'#open' => $mapping
->isNew(),
];
$salesforce_object_type = '';
if (!empty($form_state
->getValues()) && !empty($form_state
->getValue('salesforce_object_type'))) {
$salesforce_object_type = $form_state
->getValue('salesforce_object_type');
}
elseif ($mapping->salesforce_object_type) {
$salesforce_object_type = $mapping->salesforce_object_type;
}
$form['salesforce_object']['salesforce_object_type'] = [
'#title' => $this
->t('Salesforce Object'),
'#id' => 'edit-salesforce-object-type',
'#type' => 'select',
'#description' => $this
->t('Select a Salesforce object to map.'),
'#default_value' => $salesforce_object_type,
'#options' => $this
->getSalesforceObjectTypeOptions(),
'#required' => TRUE,
'#empty_option' => $this
->t('- Select -'),
];
$trigger_options = $this
->getSyncTriggerOptions();
$form['sync_triggers'] = [
'#title' => t('Action triggers'),
'#type' => 'details',
'#open' => TRUE,
'#tree' => TRUE,
'#description' => t('Select which actions on Drupal entities and Salesforce
objects should trigger a synchronization. These settings are used by the
salesforce_push and salesforce_pull modules.'),
];
if (empty($trigger_options)) {
$form['sync_triggers']['#description'] .= ' ' . $this
->t('<br/><em>No trigger options are available when Salesforce Push and Pull modules are disabled. Enable one or both modules to allow Push or Pull processing.</em>');
}
foreach ($trigger_options as $option => $label) {
$form['sync_triggers'][$option] = [
'#title' => $label,
'#type' => 'checkbox',
'#default_value' => !empty($mapping->sync_triggers[$option]),
];
}
if ($this->moduleHandler
->moduleExists('salesforce_pull')) {
$form['pull'] = [
'#title' => t('Pull Settings'),
'#type' => 'details',
'#description' => '',
'#open' => TRUE,
'#tree' => FALSE,
'#states' => [
'visible' => [
':input[name^="sync_triggers[pull"]' => [
'checked' => TRUE,
],
],
],
];
if (!$mapping
->isNew()) {
$form['pull']['last_pull_date'] = [
'#type' => 'item',
'#title' => t('Last Pull Date: %last_pull', [
'%last_pull' => $mapping
->getLastPullTime() ? \Drupal::service('date.formatter')
->format($mapping
->getLastPullTime()) : 'never',
]),
'#markup' => t('Resetting last pull date will cause salesforce pull module to query for updated records without respect for the pull trigger date. This is useful, for example, to re-pull all records after a purge.'),
];
$form['pull']['last_pull_reset'] = [
'#type' => 'button',
'#value' => t('Reset Last Pull Date'),
'#disabled' => $mapping
->getLastPullTime() == NULL,
'#limit_validation_errors' => [],
'#validate' => [
'::lastPullReset',
],
];
$form['pull']['last_delete_date'] = [
'#type' => 'item',
'#title' => t('Last Delete Date: %last_pull', [
'%last_pull' => $mapping
->getLastDeleteTime() ? \Drupal::service('date.formatter')
->format($mapping
->getLastDeleteTime()) : 'never',
]),
'#markup' => t('Resetting last delete date will cause salesforce pull module to query for deleted record without respect for the pull trigger date.'),
];
$form['pull']['last_delete_reset'] = [
'#type' => 'button',
'#value' => t('Reset Last Delete Date'),
'#disabled' => $mapping
->getLastDeleteTime() == NULL,
'#limit_validation_errors' => [],
'#validate' => [
'::lastDeleteReset',
],
];
$form['pull']['pull_trigger_date'] = [
'#type' => 'select',
'#title' => t('Date field to trigger pull'),
'#description' => t('Poll Salesforce for updated records based on the given date field. Defaults to "Last Modified Date".'),
'#required' => $mapping->salesforce_object_type,
'#default_value' => $mapping->pull_trigger_date,
'#options' => $this
->getPullTriggerOptions(),
];
}
$form['pull']['pull_where_clause'] = [
'#title' => t('Pull query SOQL "Where" clause'),
'#type' => 'textarea',
'#description' => t('Add a "where" SOQL condition clause to limit records pulled from Salesforce. e.g. Email != \'\' AND RecordType.DevelopName = \'ExampleRecordType\''),
'#default_value' => $mapping->pull_where_clause,
];
$form['pull']['pull_where_clause'] = [
'#title' => t('Pull query SOQL "Where" clause'),
'#type' => 'textarea',
'#description' => t('Add a "where" SOQL condition clause to limit records pulled from Salesforce. e.g. Email != \'\' AND RecordType.DevelopName = \'ExampleRecordType\''),
'#default_value' => $mapping->pull_where_clause,
];
$form['pull']['pull_frequency'] = [
'#title' => t('Pull Frequency'),
'#type' => 'number',
'#default_value' => $mapping->pull_frequency,
'#description' => t('Enter a frequency, in seconds, for how often this mapping should be used to pull data to Drupal. Enter 0 to pull as often as possible. FYI: 1 hour = 3600; 1 day = 86400. <em>NOTE: pull frequency is shared per-Salesforce Object. The setting is exposed here for convenience.</em>'),
];
$description = t('Check this box to disable cron pull processing for this mapping, and allow standalone processing only. A URL will be generated after saving the mapping.');
if ($mapping
->id()) {
$standalone_url = Url::fromRoute('salesforce_pull.endpoint.salesforce_mapping', [
'salesforce_mapping' => $mapping
->id(),
'key' => \Drupal::state()
->get('system.cron_key'),
], [
'absolute' => TRUE,
])
->toString();
$description = t('Check this box to disable cron pull processing for this mapping, and allow standalone processing via this URL: <a href=":url">:url</a>', [
':url' => $standalone_url,
]);
}
$form['pull']['pull_standalone'] = [
'#title' => t('Enable standalone pull queue processing'),
'#type' => 'checkbox',
'#description' => $description,
'#default_value' => $mapping->pull_standalone,
];
if ($this
->config('salesforce.settings')
->get('standalone')) {
$settings_url = Url::fromRoute('salesforce.global_settings')
->toString();
$form['pull']['pull_standalone']['#default_value'] = TRUE;
$form['pull']['pull_standalone']['#disabled'] = TRUE;
$form['pull']['pull_standalone']['#description'] .= ' ' . t('See also <a href="@url">global standalone processing settings</a>.', [
'@url' => $settings_url,
]);
}
}
if ($this->moduleHandler
->moduleExists('salesforce_push')) {
$form['push'] = [
'#title' => t('Push Settings'),
'#type' => 'details',
'#description' => t('The asynchronous push queue is always enabled in Drupal 8: real-time push fails are queued for async push. Alternatively, you can choose to disable real-time push and use async-only.'),
'#open' => TRUE,
'#tree' => FALSE,
'#states' => [
'visible' => [
':input[name^="sync_triggers[push"]' => [
'checked' => TRUE,
],
],
],
];
$form['push']['async'] = [
'#title' => t('Disable real-time push'),
'#type' => 'checkbox',
'#description' => t('When real-time push is disabled, enqueue changes and push to Salesforce asynchronously during cron. When disabled, push changes immediately upon entity CRUD, and only enqueue failures for async push.'),
'#default_value' => $mapping->async,
];
$form['push']['push_frequency'] = [
'#title' => t('Push Frequency'),
'#type' => 'number',
'#default_value' => $mapping->push_frequency,
'#description' => t('Enter a frequency, in seconds, for how often this mapping should be used to push data to Salesforce. Enter 0 to push as often as possible. FYI: 1 hour = 3600; 1 day = 86400.'),
'#min' => 0,
];
$form['push']['push_limit'] = [
'#title' => t('Push Limit'),
'#type' => 'number',
'#default_value' => $mapping->push_limit,
'#description' => t('Enter the maximum number of records to be pushed to Salesforce during a single queue batch. Enter 0 to process as many records as possible, subject to the global push queue limit.'),
'#min' => 0,
];
$form['push']['push_retries'] = [
'#title' => t('Push Retries'),
'#type' => 'number',
'#default_value' => $mapping->push_retries,
'#description' => t("Enter the maximum number of attempts to push a record to Salesforce before it's considered failed. Enter 0 for no limit."),
'#min' => 0,
];
$form['push']['weight'] = [
'#title' => t('Weight'),
'#type' => 'select',
'#options' => array_combine(range(-50, 50), range(-50, 50)),
'#description' => t('Not yet in use. During cron, mapping weight determines in which order items will be pushed. Lesser weight items will be pushed before greater weight items.'),
'#default_value' => $mapping->weight,
];
$description = t('Check this box to disable cron push processing for this mapping, and allow standalone processing. A URL will be generated after saving the mapping.');
if ($mapping
->id()) {
$standalone_url = Url::fromRoute('salesforce_push.endpoint.salesforce_mapping', [
'salesforce_mapping' => $mapping
->id(),
'key' => \Drupal::state()
->get('system.cron_key'),
], [
'absolute' => TRUE,
])
->toString();
$description = t('Check this box to disable cron push processing for this mapping, and allow standalone processing via this URL: <a href=":url">:url</a>', [
':url' => $standalone_url,
]);
}
$form['push']['push_standalone'] = [
'#title' => t('Enable standalone push queue processing'),
'#type' => 'checkbox',
'#description' => $description,
'#default_value' => $mapping->push_standalone,
];
if ($this
->config('salesforce.settings')
->get('standalone')) {
$settings_url = Url::fromRoute('salesforce.global_settings')
->toString();
$form['push']['push_standalone']['#default_value'] = TRUE;
$form['push']['push_standalone']['#disabled'] = TRUE;
$form['push']['push_standalone']['#description'] .= ' ' . t('See also <a href="@url">global standalone processing settings</a>.', [
'@url' => $settings_url,
]);
}
}
$form['meta'] = [
'#type' => 'details',
'#open' => TRUE,
'#tree' => FALSE,
'#title' => t('Additional properties'),
];
$form['meta']['weight'] = [
'#title' => t('Weight'),
'#type' => 'select',
'#options' => array_combine(range(-50, 50), range(-50, 50)),
'#description' => t('During cron, mapping weight determines in which order items will be pushed or pulled. Lesser weight items will be pushed or pulled before greater weight items.'),
'#default_value' => $mapping->weight,
];
return $form;
}
public function validateForm(array &$form, FormStateInterface $form_state) {
$bundles = $this->entityManager
->getBundleInfo($form_state
->getValue('drupal_entity_type'));
if (empty($bundles[$form_state
->getValue('drupal_bundle')])) {
$form_state
->setErrorByName('drupal_bundle', $this
->t('Invalid bundle for entity type.'));
}
$button = $form_state
->getTriggeringElement();
if ($button['#id'] != $form['actions']['submit']['#id']) {
return;
}
parent::validateForm($form, $form_state);
if ($this->entity
->doesPull()) {
try {
$this->client
->query($this->entity
->getPullQuery());
} catch (\Exception $e) {
$form_state
->setError($form['pull']['pull_where_clause'], $this
->t('Test pull query returned an error. Please check logs for error details.'));
\Drupal::service('event_dispatcher')
->dispatch(SalesforceEvents::ERROR, new SalesforceErrorEvent($e));
}
}
}
public function lastPullReset(array $form, FormStateInterface $form_state) {
$mapping = $this->entity
->setLastPullTime(NULL);
$this->entityTypeManager
->getStorage('salesforce_mapped_object')
->setForcePull($mapping);
}
public function lastDeleteReset(array $form, FormStateInterface $form_state) {
$this->entity
->setLastDeleteTime(NULL);
}
public function bundleCallback($form, FormStateInterface $form_state) {
return $form['drupal_entity']['drupal_bundle'];
}
protected function getBundleOptions() {
$entities = $this
->getEntityTypeOptions();
$bundles = $this->entityManager
->getAllBundleInfo();
$options = [];
foreach ($bundles as $entity => $bundle_info) {
if (empty($entities[$entity])) {
continue;
}
foreach ($bundle_info as $bundle => $info) {
$entity_label = $entities[$entity];
$options[(string) $entity_label][$bundle] = (string) $info['label'];
}
}
return $options;
}
protected function getEntityTypeOptions() {
$options = [];
$mappable_entity_types = $this->mappableEntityTypes
->getMappableEntityTypes();
foreach ($mappable_entity_types as $entity_type_id => $info) {
$options[$info
->id()] = $info
->getLabel();
}
uasort($options, function ($a, $b) {
return strcmp($a
->render(), $b
->render());
});
return $options;
}
protected function getSyncTriggerOptions() {
$options = [];
if ($this->moduleHandler
->moduleExists('salesforce_push')) {
$options += [
MappingConstants::SALESFORCE_MAPPING_SYNC_DRUPAL_CREATE => t('Drupal entity create (push)'),
MappingConstants::SALESFORCE_MAPPING_SYNC_DRUPAL_UPDATE => t('Drupal entity update (push)'),
MappingConstants::SALESFORCE_MAPPING_SYNC_DRUPAL_DELETE => t('Drupal entity delete (push)'),
];
}
if ($this->moduleHandler
->moduleExists('salesforce_pull')) {
$options += [
MappingConstants::SALESFORCE_MAPPING_SYNC_SF_CREATE => t('Salesforce object create (pull)'),
MappingConstants::SALESFORCE_MAPPING_SYNC_SF_UPDATE => t('Salesforce object update (pull)'),
MappingConstants::SALESFORCE_MAPPING_SYNC_SF_DELETE => t('Salesforce object delete (pull)'),
];
}
return $options;
}
private function getPullTriggerOptions() {
$options = [];
try {
$describe = $this
->getSalesforceObject();
} catch (\Exception $e) {
return [];
}
foreach ($describe
->getFields() as $field) {
if ($field['type'] == 'datetime') {
$options[$field['name']] = $field['label'];
}
}
return $options;
}
}