Commit eeaa2542 authored by mxh's avatar mxh Committed by Jürgen Haas
Browse files

Issue #3300510 by mxh: eca_form: Action "Form: add options field" cannot...

Issue #3300510 by mxh: eca_form: Action "Form: add options field" cannot handle entities as default values
parent 219ccbb4
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -67,8 +67,8 @@ abstract class FormAddFieldActionBase extends FormActionBase {
    if ($this->configuration['description'] !== '') {
      $field_element['#description'] = $this->tokenServices->replaceClear($this->configuration['description']);
    }
    if ($this->configuration['default_value'] !== '') {
      $field_element['#default_value'] = $this->tokenServices->replaceClear($this->configuration['default_value']);
    if (trim((string) $this->configuration['default_value']) !== '') {
      $field_element['#default_value'] = $this->buildDefaultValue();
    }
    return $field_element;
  }
@@ -171,4 +171,14 @@ abstract class FormAddFieldActionBase extends FormActionBase {
    $this->configuration['default_value'] = $form_state->getValue('default_value');
  }

  /**
   * Builds up the default value for the form element.
   *
   * @return mixed
   *   The default value.
   */
  protected function buildDefaultValue() {
    return $this->tokenServices->replaceClear($this->configuration['default_value']);
  }

}
+12 −4
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ class FormAddOptionsfield extends FormAddFieldActionBase {
    $element = parent::buildFieldElement();
    // Options will be filled up within ::execute().
    $element['#options'] = [];
    $is_multiple = $this->configuration['multiple'];
    $is_multiple = (bool) $this->configuration['multiple'];
    $element['#multiple'] = $is_multiple;
    if (!$is_multiple && $element['#type'] === 'checkboxes') {
      $element['#type'] = 'radios';
@@ -39,9 +39,6 @@ class FormAddOptionsfield extends FormAddFieldActionBase {
    elseif ($is_multiple && $element['#type'] === 'radios') {
      $element['#type'] = 'checkboxes';
    }
    if ($is_multiple && isset($element['#default_value']) && !is_iterable($element['#default_value'])) {
      $element['#default_value'] = DataTransferObject::buildArrayFromUserInput((string) $element['#default_value']);
    }
    return $element;
  }

@@ -110,4 +107,15 @@ class FormAddOptionsfield extends FormAddFieldActionBase {
    return $dependencies;
  }

  /**
   * {@inheritdoc}
   */
  protected function buildDefaultValue() {
    if ($default_options = $this->buildOptionsArray($this->configuration['default_value'])) {
      $is_multiple = (bool) $this->configuration['multiple'];
      return $is_multiple ? array_keys($default_options) : key($default_options);
    }
    return parent::buildDefaultValue();
  }

}
+46 −32
Original line number Diff line number Diff line
@@ -58,38 +58,7 @@ trait FormFieldSetOptionsTrait {
      }
    }
    else {
      $token = $this->tokenServices;
      $options = (mb_substr($options, 0, 1) === '[') && (mb_substr($options, -1, 1) === ']') && (mb_strlen($options) <= 255) && $token->hasTokenData($options) ? $token->getTokenData($options) : (string) $token->replaceClear($options);
      $options_array = [];
      if (is_string($options)) {
        $options_array = DataTransferObject::buildArrayFromUserInput($options);
      }
      elseif (is_iterable($options)) {
        foreach ($options as $key => $value) {
          if ($value instanceof EntityAdapter) {
            $value = $value->getValue();
          }
          if ($value instanceof EntityInterface) {
            if (!$value->isNew()) {
              $key = $value->id();
            }
            elseif ($value->uuid()) {
              $key = $value->uuid();
            }
            $value = (string) $value->label();
          }
          elseif ($value instanceof TypedDataInterface) {
            $value = $value->getString();
          }
          elseif (is_object($value) && method_exists($value, '__toString')) {
            $value = (string) $value;
          }
          if (is_scalar($value) && trim((string) $value) !== '') {
            $options_array[$key] = trim((string) $value);
          }
        }
      }
      $options = $options_array;
      $options = $this->buildOptionsArray($options);
    }

    $element['#options'] = $options;
@@ -146,4 +115,49 @@ trait FormFieldSetOptionsTrait {
    $this->yamlParser = $yaml_parser;
  }

  /**
   * Builds up an array of options, directly usable in a form element.
   *
   * @param string $input
   *  The unprocessed configuration input, which may hold a token or a fixed
   *  value, or any other sort of values.
   *
   * @return array
   *   The options array.
   */
  protected function buildOptionsArray(string $input): array {
    $token = $this->tokenServices;
    $options = (mb_substr($input, 0, 1) === '[') && (mb_substr($input, -1, 1) === ']') && (mb_strlen($input) <= 255) && $token->hasTokenData($input) ? $token->getTokenData($input) : (string) $token->replaceClear($input);
    $options_array = [];
    if (is_string($options)) {
      $options_array = DataTransferObject::buildArrayFromUserInput($options);
    }
    elseif (is_iterable($options)) {
      foreach ($options as $key => $value) {
        if ($value instanceof EntityAdapter) {
          $value = $value->getValue();
        }
        if ($value instanceof EntityInterface) {
          if (!$value->isNew()) {
            $key = $value->id();
          }
          elseif ($value->uuid()) {
            $key = $value->uuid();
          }
          $value = (string) $value->label();
        }
        elseif ($value instanceof TypedDataInterface) {
          $value = $value->getString();
        }
        elseif (is_object($value) && method_exists($value, '__toString')) {
          $value = (string) $value;
        }
        if (is_scalar($value) && trim((string) $value) !== '') {
          $options_array[$key] = trim((string) $value);
        }
      }
    }
    return $options_array;
  }

}
+120 −1
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@ class FormActionsTest extends KernelTestBase {
    $this->installConfig(static::$modules);
    User::create(['uid' => 0, 'name' => 'guest'])->save();
    User::create(['uid' => 1, 'name' => 'admin'])->save();
    User::create(['uid' => 2, 'name' => 'auth'])->save();

    // Create the Article content type with a standard body field.
    /** @var \Drupal\node\NodeTypeInterface $node_type */
@@ -349,7 +350,125 @@ class FormActionsTest extends KernelTestBase {
  }

  /**
   * Tests the action plugin "eca_form_add_optionsfield".
   * Tests the action plugin "eca_form_add_optionsfield" using checkboxes.
   */
  public function testFormAddCheckboxes(): void {
    $users = [User::load(0), User::load(1), User::load(2)];
    $this->tokenServices->addTokenData('users', $users);

    /** @var \Drupal\eca_form\Plugin\Action\FormAddOptionsfield $action */
    $action = $this->actionManager->createInstance('eca_form_add_optionsfield', [
      'name' => 'mycheckboxes',
      'type' => 'checkboxes',
      'multiple' => TRUE,
      'options' => '[users]',
      'default_value' => '1',
      'use_yaml' => FALSE,
    ]);

    /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher */
    $event_dispatcher = \Drupal::service('event_dispatcher');
    $form_builder = \Drupal::formBuilder();

    $access_result = NULL;
    $form = NULL;
    $event_dispatcher->addListener(FormEvents::PROCESS, function (FormProcess $event) use (&$access_result, &$form, $action) {
      $action->setEvent($event);
      $access_result = $access_result ?? $action->access(NULL);
      if ($action->access(NULL)) {
        $action->execute();
      }
      $form = $event->getForm();
    });

    $form_object = \Drupal::entityTypeManager()->getFormObject('node', 'default');
    $form_object->setEntity(Node::create([
      'type' => 'article',
      'title' => $this->randomMachineName(),
    ]));
    $form_state = new FormState();
    $build = $form_builder->buildForm($form_object, $form_state);

    $this->assertTrue($access_result);
    $this->assertTrue(isset($form['mycheckboxes']));
    $this->assertEquals('checkboxes', $form['mycheckboxes']['#type']);
    $this->assertSame([
      '0' => User::load(0)->label(),
      '1' => User::load(1)->label(),
      '2' => User::load(2)->label(),
    ], $form['mycheckboxes']['#options']);

    /** @var \Drupal\Core\Render\RendererInterface $renderer */
    $renderer = \Drupal::service('renderer');
    $rendered = $renderer->renderPlain($build);
    $this->assertStringContainsString('name="mycheckboxes[0]" value="0"', $rendered);
    $this->assertStringNotContainsString('name="mycheckboxes[0]" value="0" checked="checked"', $rendered);
    $this->assertStringContainsString('name="mycheckboxes[1]" value="1" checked="checked"', $rendered);
    $this->assertStringContainsString('name="mycheckboxes[2]" value="2"', $rendered);
    $this->assertStringNotContainsString('name="mycheckboxes[2]" value="2" checked="checked"', $rendered);
  }

  /**
   * Tests the action plugin "eca_form_add_optionsfield" using checkboxes.
   *
   * Default values for the checkboxes are entities.
   */
  public function testFormAddCheckboxesDefaultValueEntities(): void {
    $users = [User::load(0), User::load(1), User::load(2)];
    $this->tokenServices->addTokenData('users', $users);

    /** @var \Drupal\eca_form\Plugin\Action\FormAddOptionsfield $action */
    $action = $this->actionManager->createInstance('eca_form_add_optionsfield', [
      'name' => 'mycheckboxes',
      'type' => 'checkboxes',
      'multiple' => TRUE,
      'options' => '[users]',
      'default_value' => '[users]',
      'use_yaml' => FALSE,
    ]);

    /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher */
    $event_dispatcher = \Drupal::service('event_dispatcher');
    $form_builder = \Drupal::formBuilder();

    $access_result = NULL;
    $form = NULL;
    $event_dispatcher->addListener(FormEvents::PROCESS, function (FormProcess $event) use (&$access_result, &$form, $action) {
      $action->setEvent($event);
      $access_result = $access_result ?? $action->access(NULL);
      if ($action->access(NULL)) {
        $action->execute();
      }
      $form = $event->getForm();
    });

    $form_object = \Drupal::entityTypeManager()->getFormObject('node', 'default');
    $form_object->setEntity(Node::create([
      'type' => 'article',
      'title' => $this->randomMachineName(),
    ]));
    $form_state = new FormState();
    $build = $form_builder->buildForm($form_object, $form_state);

    $this->assertTrue($access_result);
    $this->assertTrue(isset($form['mycheckboxes']));
    $this->assertEquals('checkboxes', $form['mycheckboxes']['#type']);
    $this->assertSame([
      '0' => User::load(0)->label(),
      '1' => User::load(1)->label(),
      '2' => User::load(2)->label(),
    ], $form['mycheckboxes']['#options']);

    /** @var \Drupal\Core\Render\RendererInterface $renderer */
    $renderer = \Drupal::service('renderer');
    $rendered = $renderer->renderPlain($build);
    $this->assertStringContainsString('name="mycheckboxes[0]" value="0" checked="checked"', $rendered);
    $this->assertStringContainsString('name="mycheckboxes[1]" value="1" checked="checked"', $rendered);
    $this->assertStringContainsString('name="mycheckboxes[2]" value="2" checked="checked"', $rendered);
  }

  /**
   * Tests the action plugin "eca_form_add_submit_button".
   */
  public function testFormAddSubmitButton(): void {
    /** @var \Drupal\eca_form\Plugin\Action\FormAddSubmitButton $action */