View source
<?php
namespace Drupal\conditional_fields\Form;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\conditional_fields\ConditionalFieldsInterface;
use Drupal\conditional_fields\Conditions;
use Drupal\conditional_fields\DependencyHelper;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ConditionalFieldEditForm extends FormBase {
protected $redirectPath = 'conditional_fields.conditions_list';
protected $list;
protected $entityTypeManager;
protected $formBuilder;
public function __construct(Conditions $list, EntityTypeManagerInterface $entity_type_manager, FormBuilderInterface $form_builder) {
$this->list = $list;
$this->entityTypeManager = $entity_type_manager;
$this->formBuilder = $form_builder;
}
public static function create(ContainerInterface $container) {
return new static($container
->get('conditional_fields.conditions'), $container
->get('entity_type.manager'), $container
->get('form_builder'));
}
public function getFormId() {
return 'conditional_field_edit_form';
}
public function buildForm(array $form, FormStateInterface $form_state, $entity_type = NULL, $bundle = NULL, $field_name = NULL, $uuid = NULL) {
if (empty($entity_type) || empty($bundle) || empty($field_name) || empty($uuid)) {
return $form;
}
$form_display_entity = $this->entityTypeManager
->getStorage('entity_form_display')
->load("{$entity_type}.{$bundle}.default");
if (!$form_display_entity) {
return $form;
}
$field = count(explode('-', $field_name)) > 0 ? explode('-', $field_name)[0] : $field_name;
$field = $form_display_entity
->getComponent($field);
if (empty($field['third_party_settings']['conditional_fields'][$uuid])) {
return $form;
}
$condition = $field['third_party_settings']['conditional_fields'][$uuid];
$settings = $condition['settings'];
$label = $condition['dependee'];
$form['submit'] = [
'#type' => 'submit',
'#value' => $this
->t('Save settings'),
'#weight' => 50,
];
$form['entity_type'] = [
'#type' => 'textfield',
'#value' => $entity_type,
'#access' => FALSE,
];
$form['bundle'] = [
'#type' => 'textfield',
'#value' => $bundle,
'#access' => FALSE,
];
$form['field_name'] = [
'#type' => 'textfield',
'#value' => $field_name,
'#access' => FALSE,
];
$form['uuid'] = [
'#type' => 'textfield',
'#value' => $uuid,
'#access' => FALSE,
];
$form['condition'] = [
'#type' => 'select',
'#title' => $this
->t('Condition'),
'#description' => $this
->t('The condition that should be met by the control field %field to trigger the dependency.', [
'%field' => $label,
]),
'#options' => $this->list
->conditionalFieldsConditions(),
'#default_value' => array_key_exists('condition', $settings) ? $settings['condition'] : '',
'#required' => TRUE,
];
$form['values_set'] = [
'#type' => 'select',
'#title' => $this
->t('Values input mode'),
'#description' => $this
->t('The input mode of the values that trigger the dependency.'),
'#options' => [
ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET => $this
->t('Insert value from widget...'),
ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_REGEX => $this
->t('Regular expression...'),
'Set of values' => [
ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND => $this
->t('All these values (AND)...'),
ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_OR => $this
->t('Any of these values (OR)...'),
ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR => $this
->t('Only one of these values (XOR)...'),
ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT => $this
->t('None of these values (NOT)...'),
],
],
'#default_value' => array_key_exists('values_set', $settings) ? $settings['values_set'] : 0,
'#required' => TRUE,
'#states' => [
'visible' => [
':input[name="condition"]' => [
'value' => 'value',
],
],
],
];
$dummy_field = $this
->getDummyField($entity_type, $bundle, $condition, $form_state, $settings['value_form']);
$form['value'] = [
'#type' => 'fieldset',
'#title' => $this
->t('Insert value from widget'),
'#description' => $this
->t('The dependency is triggered when the field has exactly the same value(s) inserted in the widget below.'),
'#states' => [
'visible' => [
':input[name="values_set"]' => [
'value' => (string) ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET,
],
':input[name="condition"]' => [
'value' => 'value',
],
],
'required' => [
':input[name="values_set"]' => [
'value' => (string) ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET,
],
':input[name="condition"]' => [
'value' => 'value',
],
],
],
'#tree' => FALSE,
'field' => $dummy_field,
];
$form['values'] = [
'#type' => 'textarea',
'#title' => $this
->t('Set of values'),
'#description' => $this
->t('The values of the dependee %field that trigger the dependency.', [
'%field' => $label,
]) . '<br>' . $this
->t('Enter one value per line. Note: if the dependee has allowed values, these are actually the keys, not the labels, of those values.'),
'#states' => [
'visible' => [
':input[name="values_set"]' => [
[
'value' => (string) ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND,
],
[
'value' => (string) ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_OR,
],
[
'value' => (string) ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR,
],
[
'value' => (string) ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT,
],
],
':input[name="condition"]' => [
'value' => 'value',
],
],
'required' => [
':input[name="values_set"]' => [
[
'value' => (string) ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND,
],
[
'value' => (string) ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_OR,
],
[
'value' => (string) ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR,
],
[
'value' => (string) ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT,
],
],
':input[name="condition"]' => [
'value' => 'value',
],
],
],
];
if (!empty($settings['values']) && is_array($settings['values'])) {
$form['values']['#default_value'] = implode("\n", $settings['values']);
}
elseif (!empty($settings['values']) && is_string($settings['values'])) {
$form['values']['#default_value'] = $settings['values'];
}
else {
$form['values']['#default_value'] = '';
}
$form['regex'] = [
'#type' => 'textfield',
'#title' => $this
->t('Regular expression'),
'#description' => $this
->t('The dependency is triggered when all the values of the dependee %field match the regular expression. The expression should be valid both in PHP and in Javascript. Do not include delimiters.', [
'%field' => $label,
]) . '<br>' . $this
->t('Note: If the dependee has allowed values, these are actually the keys, not the labels, of those values.'),
'#maxlength' => 2048,
'#size' => 120,
'#default_value' => isset($settings['regex']) ? $settings['regex'] : '',
'#states' => [
'visible' => [
':input[name="values_set"]' => [
'value' => (string) ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_REGEX,
],
':input[name="condition"]' => [
'value' => 'value',
],
],
'required' => [
':input[name="values_set"]' => [
'value' => (string) ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_REGEX,
],
':input[name="condition"]' => [
'value' => 'value',
],
],
],
];
$form['grouping'] = [
'#type' => 'radios',
'#title' => $this
->t('Interaction with other dependencies'),
'#description' => $this
->t('When this target field has more than one control field, how should this condition be evaluated against the others?') . '<br />' . $this
->t('Note that sets will be grouped this way: (ANDs) AND (ORs) AND (XORs).'),
'#options' => [
'AND' => 'AND',
'OR' => 'OR',
'XOR' => 'XOR',
],
'#default_value' => array_key_exists('grouping', $settings) ? $settings['grouping'] : 'AND',
'#required' => TRUE,
];
if ($this
->fieldSupportsInheritance($entity_type, $bundle, $field_name)) {
$form['inheritance'] = [
'#type' => 'checkboxes',
'#title' => $this
->t('Inheritance'),
'#description' => $this
->t('This element contains other fields. Apply the settings in this form to the contained fields instead of this one.'),
'#options' => [
'propagate' => $this
->t('Propagate settings to fields contained within this one.'),
'apply_to_parent' => $this
->t('Apply these settings to the this (parent) field also. Requires the "Propagate" setting, above.'),
'recurse' => $this
->t('Apply these settings to group fields contained within this one. Requires the "Propagate" setting, above.'),
],
'#default_value' => array_key_exists('inheritance', $settings) ? $settings['inheritance'] : [],
];
$form['inheritance']['apply_to_parent'] = [
'#states' => [
'disabled' => [
':input[name="inheritance[propagate]"]' => [
'checked' => FALSE,
],
],
],
];
$form['inheritance']['recurse']['#states'] = $form['inheritance']['apply_to_parent']['#states'];
}
$form['entity_edit'] = [
'#type' => 'details',
'#title' => $this
->t('Edit context settings'),
'#description' => $this
->t('These settings apply when the @entity is being added or edited in a form.', [
'@entity' => $label,
]),
'#open' => TRUE,
];
$form['entity_edit'] += $this
->buildEditContextSettings([], $form_state, $condition);
return $form;
}
public function validateForm(array &$form, FormStateInterface $form_state) {
$condition = $form_state
->getValue('condition');
$allowed_values_set = [
ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND,
ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_OR,
ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR,
ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT,
];
if ($condition == 'value') {
if (in_array($form_state
->getValue('values_set'), $allowed_values_set) && mb_strlen(trim($form_state
->getValue('values')) === 0)) {
$form_state
->setErrorByName('values', $this
->t('Field %name is required.', [
'%name' => $this
->t('Set of values'),
]));
}
elseif ($form_state
->getValue('values_set') == ConditionalFieldsInterface::CONDITIONAL_FIELDS_DEPENDENCY_VALUES_REGEX && mb_strlen(trim($form_state
->getValue('regex'))) == 0) {
$form_state
->setErrorByName('regex', $this
->t('Field %name is required.', [
'%name' => $this
->t('Regular expression'),
]));
}
}
if (!in_array($condition, [
'!empty',
'empty',
'value',
'checked',
'!checked',
])) {
$form_state
->setValue('reset', 0);
}
parent::validateForm($form, $form_state);
}
public function submitForm(array &$form, FormStateInterface $form_state) {
$values = $form_state
->cleanValues()
->getValues();
$uuid = $values['uuid'];
$entity_type = $values['entity_type'];
$bundle = $values['bundle'];
$entity = $this->entityTypeManager
->getStorage('entity_form_display')
->load("{$entity_type}.{$bundle}.default");
if (!$entity) {
return;
}
$field_names = explode('-', $values['field_name']);
foreach ($field_names as $field_name) {
$field = $entity
->getComponent($field_name);
$settings =& $field['third_party_settings']['conditional_fields'][$uuid]['settings'];
$dependee = $field['third_party_settings']['conditional_fields'][$uuid]['dependee'];
$exclude_fields = [
'entity_type',
'bundle',
'field_name',
'uuid',
];
foreach ($values as $key => $value) {
if (in_array($key, $exclude_fields) || empty($value)) {
continue;
}
else {
$settings[$key] = $value;
}
}
if (isset($settings['reset']) && !empty($settings['reset'])) {
$settings['reset'] = $values['reset'];
}
else {
$settings['reset'] = 0;
}
if ($settings['effect'] == 'show') {
$settings['effect_options'] = [];
}
if (isset($settings[$dependee]) && !empty($settings[$dependee])) {
$value =& $settings[$dependee];
if (!empty($value[0]['value']) && is_object($value[0]['value']) && $value[0]['value'] instanceof DrupalDateTime) {
foreach ($value as $delta => $date) {
if (!empty($date['value'])) {
$value[$delta]['value'] = $date['value']
->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT);
}
}
}
$settings['value_form'] = $settings[$dependee];
unset($settings[$dependee]);
}
$entity
->setComponent($field_name, $field);
}
$entity
->save();
$parameters = [
'entity_type' => $values['entity_type'],
'bundle' => $values['bundle'],
];
$form_state
->setRedirect($this->redirectPath, $parameters);
}
public function buildEditContextSettings(array $form, FormStateInterface $form_state, $condition) {
$label = array_key_exists('dependee', $condition) ? $condition['dependee'] : '?';
$settings = array_key_exists('settings', $condition) ? $condition['settings'] : [];
$form['state'] = [
'#type' => 'select',
'#title' => $this
->t('Form state'),
'#description' => $this
->t('The Javascript form state that is applied to the target field when the condition is met. Note: this has no effect on server-side logic and validation.'),
'#options' => $this->list
->conditionalFieldsStates(),
'#default_value' => array_key_exists('state', $settings) ? $settings['state'] : 0,
'#required' => TRUE,
'#ajax' => [
'callback' => '::ajaxAdminStateCallback',
'wrapper' => 'effects-wrapper',
],
];
$effects = $effects_options = [];
$selected_state = $form_state
->getValue('state') ?: $condition['settings']['state'];
foreach ($this->list
->conditionalFieldsEffects() as $effect_name => $effect) {
if (empty($selected_state)) {
continue;
}
if (in_array($selected_state, $effect['states'])) {
$effects[$effect_name] = $effect['label'];
if (isset($effect['options'])) {
$effects_options[$effect_name] = $effect['options'];
}
}
}
$form['effects_wrapper'] = [
'#type' => 'container',
'#attributes' => [
'id' => 'effects-wrapper',
],
];
$effect = array_key_exists('effect', $settings) ? $settings['effect'] : '';
$effect = $form_state
->hasValue('effect') ? $form_state
->getValue('effect') : $effect;
if (count($effects) == 1) {
$effects_keys = array_keys($effects);
$form['effects_wrapper']['effect'] = [
'#type' => 'hidden',
'#value' => array_shift($effects_keys),
'#default_value' => array_shift($effects_keys),
];
}
elseif (count($effects) > 1) {
$form['effects_wrapper']['effect'] = [
'#type' => 'select',
'#title' => $this
->t('Effect'),
'#description' => $this
->t('The effect that is applied to the target field when its state is changed.'),
'#options' => $effects,
'#default_value' => $effect,
'#states' => [
'visible' => [
':input[name="state"]' => [
[
'value' => 'visible',
],
[
'value' => '!visible',
],
],
],
],
];
}
$form['effects_wrapper']['effect_options'] = [
'#tree' => TRUE,
];
foreach ($effects_options as $effect_name => $effect_options) {
foreach ($effect_options as $effect_option_name => $effect_option) {
$effect_option += [
'#title' => $this
->t('@effect effect option: @effect_option', [
'@effect' => $effects[$effect_name],
'@effect_option' => $effect_option_name,
]),
'#states' => [
'visible' => [
':input[name="effect"]' => [
[
'value' => $effect_name,
],
],
],
],
];
if (isset($form_state
->getValue('effect_options')[$effect_name][$effect_option_name])) {
$effect_option['#default_value'] = $form_state
->getValue('effect_options')[$effect_name][$effect_option_name];
}
elseif ($settings['effect'] == $effect_name) {
$effect_option['#default_value'] = $settings['effect_options'][$effect_name][$effect_option_name];
}
$form['effects_wrapper']['effect_options'][$effect_name][$effect_option_name] = $effect_option;
}
}
$form['reset'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Reset the target to its default values when the form is submitted if the dependency is not triggered.'),
'#default_value' => array_key_exists('reset', $settings) ? $settings['reset'] : 0,
'#states' => [
'visible' => [
':input[name="condition"]' => [
[
'value' => '!empty',
],
[
'value' => 'empty',
],
[
'value' => 'value',
],
[
'value' => 'checked',
],
[
'value' => '!checked',
],
],
],
],
'#description' => $this
->t('Note: This setting only applies if the condition is "Value", "Empty", "Checked", "Unchecked", or "Filled" and may not work with some field types. Also, ensure that the default values are valid, since they will not be validated.'),
];
$form['dependency_advanced'] = [
'#type' => 'details',
'#title' => $this
->t('Advanced edit context settings', [
'@entity' => $label,
]),
'#open' => FALSE,
];
$selector_description = $this
->t('Only use if you know what you are doing, otherwise leave the field empty to let the dependency use an automatically generated selector.');
$selector_description .= '<br />' . $this
->t('You can use the following placeholders:');
$selector_description .= "<ul>\n";
$selector_description .= '<li>' . $this
->t('%lang: current language of the field.') . "</li>\n";
$selector_description .= '<li>' . $this
->t('%key: part identifier for fields composed of multiple form elements, like checkboxes.') . "</li>\n";
$selector_description .= '</ul>';
$form['dependency_advanced']['selector'] = [
'#type' => 'textfield',
'#title' => $this
->t('Custom jQuery selector for control field'),
'#description' => $selector_description,
'#default_value' => array_key_exists('selector', $settings) ? $settings['selector'] : '',
];
return $form;
}
public function ajaxAdminStateCallback(array $form, FormStateInterface $form_state) {
return $form['entity_edit']['effects_wrapper'];
}
protected function getDummyField($entity_type, $bundle, $condition, FormStateInterface $form_state, $default_value = NULL) {
$field_name = $condition['dependee'];
$dummy_field = [];
$entityTypeManager = $this->entityTypeManager;
$storage = $entityTypeManager
->getStorage($entity_type);
$bundle_key = $storage
->getEntityType()
->getKey('bundle');
$dummy_entity = $storage
->create([
'uid' => $this
->currentUser()
->id(),
$bundle_key => $bundle,
]);
if ($default_value) {
$dummy_entity
->set($field_name, $default_value);
}
try {
$handlers = $entityTypeManager
->getDefinition($entity_type)
->getHandlerClasses();
$operation = isset($handlers['form']['edit']) ? 'edit' : 'default';
$form_object = $entityTypeManager
->getFormObject($entity_type, $operation);
$form_object
->setEntity($dummy_entity);
} catch (InvalidPluginDefinitionException $e) {
watchdog_exception('conditional_fields', $e);
return NULL;
}
$form_builder_service = $this->formBuilder;
$form_state_additions = [];
$form_state_new = (new FormState())
->setFormState($form_state_additions);
if ($form_state
->isMethodType("POST")) {
$form_state_new
->setRequestMethod("POST");
}
$user_input = $form_state
->getUserInput();
if (isset($user_input[$field_name])) {
$form_state_new
->setUserInput([
$field_name => $user_input[$field_name],
]);
$form_state_new
->setProgrammed(TRUE);
$form_state_new
->setValidationComplete(TRUE);
}
$dummy_entity_form = $form_builder_service
->buildForm($form_object, $form_state_new);
if (isset($dummy_entity_form[$field_name])) {
$dummy_field = $dummy_entity_form[$field_name];
$this
->setFieldProperty($dummy_field, '#required', FALSE);
}
return $dummy_field;
}
protected function setFieldProperty(&$field, $property, $value) {
$elements = Element::children($field);
if (isset($elements) && count($elements) > 0) {
foreach ($elements as $element) {
$field[$element][$property] = $value;
$this
->setFieldProperty($field[$element], $property, $value);
}
}
}
protected function fieldSupportsInheritance($entity_type, $bundle, $field_name) {
$dependency_helper = new DependencyHelper($entity_type, $bundle);
return $dependency_helper
->fieldHasChildren($field_name);
}
}