Version 1
[yaffs-website] / web / modules / contrib / ctools / src / Wizard / FormWizardBase.php
diff --git a/web/modules/contrib/ctools/src/Wizard/FormWizardBase.php b/web/modules/contrib/ctools/src/Wizard/FormWizardBase.php
new file mode 100644 (file)
index 0000000..e537ef8
--- /dev/null
@@ -0,0 +1,465 @@
+<?php
+
+namespace Drupal\ctools\Wizard;
+
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\CloseModalDialogCommand;
+use Drupal\Core\DependencyInjection\ClassResolverInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Form\FormInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Url;
+use Drupal\ctools\Ajax\OpenModalWizardCommand;
+use Drupal\ctools\Event\WizardEvent;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+
+/**
+ * The base class for all form wizard.
+ */
+abstract class FormWizardBase extends FormBase implements FormWizardInterface {
+
+  /**
+   * Tempstore Factory for keeping track of values in each step of the wizard.
+   *
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstore;
+
+  /**
+   * The Form Builder.
+   *
+   * @var \Drupal\Core\Form\FormBuilderInterface
+   */
+  protected $builder;
+
+  /**
+   * The class resolver.
+   *
+   * @var \Drupal\Core\DependencyInjection\ClassResolverInterface;
+   */
+  protected $classResolver;
+
+  /**
+   * The event dispatcher.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $dispatcher;
+
+  /**
+   * The shared temp store factory collection name.
+   *
+   * @var string
+   */
+  protected $tempstore_id;
+
+  /**
+   * The SharedTempStore key for our current wizard values.
+   *
+   * @var string|NULL
+   */
+  protected $machine_name;
+
+  /**
+   * The current active step of the wizard.
+   *
+   * @var string|NULL
+   */
+  protected $step;
+
+  /**
+   * @param \Drupal\user\SharedTempStoreFactory $tempstore
+   *   Tempstore Factory for keeping track of values in each step of the
+   *   wizard.
+   * @param \Drupal\Core\Form\FormBuilderInterface $builder
+   *   The Form Builder.
+   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
+   *   The class resolver.
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
+   *   The event dispatcher.
+   * @param $tempstore_id
+   *   The shared temp store factory collection name.
+   * @param null $machine_name
+   *   The SharedTempStore key for our current wizard values.
+   * @param null $step
+   *   The current active step of the wizard.
+   */
+  public function __construct(SharedTempStoreFactory $tempstore, FormBuilderInterface $builder, ClassResolverInterface $class_resolver, EventDispatcherInterface $event_dispatcher, RouteMatchInterface $route_match, $tempstore_id, $machine_name = NULL, $step = NULL) {
+    $this->tempstore = $tempstore;
+    $this->builder = $builder;
+    $this->classResolver = $class_resolver;
+    $this->dispatcher = $event_dispatcher;
+    $this->routeMatch = $route_match;
+    $this->tempstore_id = $tempstore_id;
+    $this->machine_name = $machine_name;
+    $this->step = $step;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getParameters() {
+    return [
+      'tempstore' => \Drupal::service('user.shared_tempstore'),
+      'builder' => \Drupal::service('form_builder'),
+      'class_resolver' => \Drupal::service('class_resolver'),
+      'event_dispatcher' => \Drupal::service('event_dispatcher'),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function initValues() {
+    $values = [];
+    $event = new WizardEvent($this, $values);
+    $this->dispatcher->dispatch(FormWizardInterface::LOAD_VALUES, $event);
+    return $event->getValues();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTempstoreId() {
+    return $this->tempstore_id;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTempstore() {
+    return $this->tempstore->get($this->getTempstoreId());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMachineName() {
+    return $this->machine_name;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getStep($cached_values) {
+    if (!$this->step) {
+      $operations = $this->getOperations($cached_values);
+      $steps = array_keys($operations);
+      $this->step = reset($steps);
+    }
+    return $this->step;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOperation($cached_values) {
+    $operations = $this->getOperations($cached_values);
+    $step = $this->getStep($cached_values);
+    if (!empty($operations[$step])) {
+      return $operations[$step];
+    }
+    $operation = reset($operations);
+    return $operation;
+  }
+
+  /**
+   * The translated text of the "Next" button's text.
+   *
+   * @return string
+   */
+  public function getNextOp() {
+    return $this->t('Next');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getNextParameters($cached_values) {
+    // Get the steps by key.
+    $operations = $this->getOperations($cached_values);
+    $steps = array_keys($operations);
+    // Get the steps after the current step.
+    $after = array_slice($operations, array_search($this->getStep($cached_values), $steps) + 1);
+    // Get the steps after the current step by key.
+    $after_keys = array_keys($after);
+    $step = reset($after_keys);
+    if (!$step) {
+      $keys = array_keys($operations);
+      $step = end($keys);
+    }
+    return [
+      'machine_name' => $this->getMachineName(),
+      'step' => $step,
+      'js' => 'nojs',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPreviousParameters($cached_values) {
+    $operations = $this->getOperations($cached_values);
+    $step = $this->getStep($cached_values);
+
+    // Get the steps by key.
+    $steps = array_keys($operations);
+    // Get the steps before the current step.
+    $before = array_slice($operations, 0, array_search($step, $steps));
+    // Get the steps before the current step by key.
+    $before = array_keys($before);
+    // Reverse the steps for easy access to the next step.
+    $before_steps = array_reverse($before);
+    $step = reset($before_steps);
+    return [
+      'machine_name' => $this->getMachineName(),
+      'step' => $step,
+      'js' => 'nojs',
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    if (!$this->getMachineName() || !$this->getTempstore()->get($this->getMachineName())) {
+      $cached_values = $this->initValues();
+    }
+    else {
+      $cached_values = $this->getTempstore()->get($this->getMachineName());
+    }
+    $operation = $this->getOperation($cached_values);
+    /* @var $operation \Drupal\Core\Form\FormInterface */
+    $operation = $this->classResolver->getInstanceFromDefinition($operation['form']);
+    return $operation->getFormId();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    // Get the current form operation.
+    $operation = $this->getOperation($cached_values);
+    $form = $this->customizeForm($form, $form_state);
+    /* @var $formClass \Drupal\Core\Form\FormInterface */
+    $formClass = $this->classResolver->getInstanceFromDefinition($operation['form']);
+    // Pass include any custom values for this operation.
+    if (!empty($operation['values'])) {
+      $cached_values = array_merge($cached_values, $operation['values']);
+      $form_state->setTemporaryValue('wizard', $cached_values);
+    }
+    // Build the form.
+    $form = $formClass->buildForm($form, $form_state);
+    if (isset($operation['title'])) {
+      $form['#title'] = $operation['title'];
+    }
+    $form['actions'] = $this->actions($formClass, $form_state);
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    // Only perform this logic if we're moving to the next page. This prevents
+    // the loss of cached values on ajax submissions.
+    if ((string)$form_state->getValue('op') == (string)$this->getNextOp()) {
+      $cached_values = $form_state->getTemporaryValue('wizard');
+      if ($form_state->hasValue('label')) {
+        $cached_values['label'] = $form_state->getValue('label');
+      }
+      if ($form_state->hasValue('id')) {
+        $cached_values['id'] = $form_state->getValue('id');
+      }
+      if (is_null($this->machine_name) && !empty($cached_values['id'])) {
+        $this->machine_name = $cached_values['id'];
+      }
+      $this->getTempstore()->set($this->getMachineName(), $cached_values);
+      if (!$form_state->get('ajax')) {
+        $form_state->setRedirect($this->getRouteName(), $this->getNextParameters($cached_values));
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function populateCachedValues(array &$form, FormStateInterface $form_state) {
+    $cached_values = $this->getTempstore()->get($this->getMachineName());
+    if (!$cached_values) {
+      $cached_values = $form_state->getTemporaryValue('wizard');
+      if (!$cached_values) {
+        $cached_values = $this->initValues();
+        $form_state->setTemporaryValue('wizard', $cached_values);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function previous(array &$form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $form_state->setRedirect($this->getRouteName(), $this->getPreviousParameters($cached_values));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function finish(array &$form, FormStateInterface $form_state) {
+    $this->getTempstore()->delete($this->getMachineName());
+  }
+
+  /**
+   * Helper function for generating default form elements.
+   *
+   * @param array $form
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *
+   * @return array
+   */
+  protected function customizeForm(array $form, FormStateInterface $form_state) {
+    // Setup the step rendering theme element.
+    $prefix = [
+      '#theme' => ['ctools_wizard_trail'],
+      '#wizard' => $this,
+      '#cached_values' => $form_state->getTemporaryValue('wizard'),
+    ];
+    // @todo properly inject the renderer.
+    $form['#prefix'] = \Drupal::service('renderer')->render($prefix);
+    return $form;
+  }
+
+  /**
+   * Generates action elements for navigating between the operation steps.
+   *
+   * @param \Drupal\Core\Form\FormInterface $form_object
+   *   The current operation form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current form state.
+   *
+   * @return array
+   */
+  protected function actions(FormInterface $form_object, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $operations = $this->getOperations($cached_values);
+    $step = $this->getStep($cached_values);
+    $operation = $operations[$step];
+
+    $steps = array_keys($operations);
+    // Slice to find the operations that occur before the current operation.
+    $before = array_slice($operations, 0, array_search($step, $steps));
+    // Slice to find the operations that occur after the current operation.
+    $after = array_slice($operations, array_search($step, $steps) + 1);
+
+    $actions = [
+      'submit' => [
+        '#type' => 'submit',
+        '#value' => $this->t('Next'),
+        '#button_type' => 'primary',
+        '#validate' => [
+          '::populateCachedValues',
+          [$form_object, 'validateForm'],
+        ],
+        '#submit' => [
+          [$form_object, 'submitForm'],
+        ],
+      ],
+    ];
+
+    // Add any submit or validate functions for the step and the global ones.
+    if (isset($operation['validate'])) {
+      $actions['submit']['#validate'] = array_merge($actions['submit']['#validate'], $operation['validate']);
+    }
+    $actions['submit']['#validate'][] = '::validateForm';
+    if (isset($operation['submit'])) {
+      $actions['submit']['#submit'] = array_merge($actions['submit']['#submit'], $operation['submit']);
+    }
+    $actions['submit']['#submit'][] = '::submitForm';
+
+    if ($form_state->get('ajax')) {
+      // Ajax submissions need to submit to the current step, not "next".
+      $parameters = $this->getNextParameters($cached_values);
+      $parameters['step'] = $this->getStep($cached_values);
+      $actions['submit']['#ajax'] = [
+        'callback' => '::ajaxSubmit',
+        'url' => Url::fromRoute($this->getRouteName(), $parameters),
+        'options' => ['query' => \Drupal::request()->query->all() + [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]],
+      ];
+    }
+
+    // If there are steps before this one, label the button "previous"
+    // otherwise do not display a button.
+    if ($before) {
+      $actions['previous'] = array(
+        '#type' => 'submit',
+        '#value' => $this->t('Previous'),
+        '#validate' => array(
+          array($this, 'populateCachedValues'),
+        ),
+        '#submit' => array(
+          array($this, 'previous'),
+        ),
+        '#limit_validation_errors' => array(),
+        '#weight' => -10,
+      );
+      if ($form_state->get('ajax')) {
+        // Ajax submissions need to submit to the current step, not "previous".
+        $parameters = $this->getPreviousParameters($cached_values);
+        $parameters['step'] = $this->getStep($cached_values);
+        $actions['previous']['#ajax'] = [
+          'callback' => '::ajaxPrevious',
+          'url' => Url::fromRoute($this->getRouteName(), $parameters),
+          'options' => ['query' => \Drupal::request()->query->all() + [FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]],
+        ];
+      }
+    }
+
+    // If there are not steps after this one, label the button "Finish".
+    if (!$after) {
+      $actions['submit']['#value'] = $this->t('Finish');
+      $actions['submit']['#submit'][] = array($this, 'finish');
+      if ($form_state->get('ajax')) {
+        $actions['submit']['#ajax']['callback'] = [$this, 'ajaxFinish'];
+      }
+    }
+
+    return $actions;
+  }
+
+  public function ajaxSubmit(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $response = new AjaxResponse();
+    $parameters = $this->getNextParameters($cached_values);
+    $response->addCommand(new OpenModalWizardCommand($this, $this->getTempstoreId(), $parameters));
+    return $response;
+  }
+
+  public function ajaxPrevious(array $form, FormStateInterface $form_state) {
+    $cached_values = $form_state->getTemporaryValue('wizard');
+    $response = new AjaxResponse();
+    $parameters = $this->getPreviousParameters($cached_values);
+    $response->addCommand(new OpenModalWizardCommand($this, $this->getTempstoreId(), $parameters));
+    return $response;
+  }
+
+  public function ajaxFinish(array $form, FormStateInterface $form_state) {
+    $response = new AjaxResponse();
+    $response->addCommand(new CloseModalDialogCommand());
+    return $response;
+  }
+
+  public function getRouteName() {
+    return $this->routeMatch->getRouteName();
+  }
+
+}