diff --git a/core/lib/Drupal/Core/Wizard/WizardFormController.php b/core/lib/Drupal/Core/Wizard/WizardFormController.php new file mode 100644 index 0000000..060cbe1 --- /dev/null +++ b/core/lib/Drupal/Core/Wizard/WizardFormController.php @@ -0,0 +1,179 @@ +entityInfo = $entity->entityInfo(); + } + + /** + * Overrides Drupal\Core\Entity\EntityFormController::actions(). + * + * Digs through the entity definition's steps to determine if previous or + * next steps exist in order to display the appropriate buttons and text on + * those buttons. + */ + protected function actions(array $form, array &$form_state) { + $actions = parent::actions($form, $form_state); + $entity = $this->buildEntity($form, $form_state += array('values' => array())); + // Get the steps of the wizard from the entity definition. + $steps = array_keys($this->entityInfo[$this->stepKey]); + // Slice to find the operations that occur before the current operation. + $before = array_slice($this->entityInfo[$this->stepKey], 0, array_search($this->getOperation(), $steps)); + // Slice to find the operations that occur after the current operation. + $after = array_slice($this->entityInfo[$this->stepKey], array_search($this->getOperation(), $steps) + 1); + + // If there are steps after this one, label the button "Next" otherwise + // label it "Finish". + if ($after) { + $actions['submit']['#value'] = t('Next'); + } + else { + $actions['submit']['#value'] = t('Finish'); + $actions['submit']['#submit'][] = array($this, 'finish'); + } + + // If there are steps before this one, label the button "previous" + // otherwise do not display a button. + if ($before) { + $actions['previous'] = array( + '#value' => t('Previous'), + '#submit' => array( + array($this, 'previous'), + ), + '#limit_validation_errors' => array(), + '#weight' => -10, + ); + } + + return $actions; + } + + /** + * Overrides Drupal\Core\Entity\EntityFormController::submit(). + * + * Instead of saving the entity at each step, we populate a tempstore with + * the entity for the next step. + */ + public function submit(array $form, array &$form_state) { + if ($form_state['values']['op'] == 'Next') { + $entity = $this->buildEntity($form, $form_state); + drupal_container()->get('user.tempstore')->get($entity->entityType())->set($entity->id(), $entity); + // Get the steps by key. + $steps = array_keys($this->entityInfo[$this->stepKey]); + // Get the steps after the current step. + $after = array_slice($this->entityInfo[$this->stepKey], array_search($this->getOperation(), $steps) + 1); + // Get the steps after the current step by key. + $after = array_keys($after); + + $form_state['redirect'] = $this->redirect($entity, $after[0]); + } + } + + /** + * Form submission handler for the 'previous' action. + * + * This method finds the previous step and redirects us to it. + * + * @param array $form + * An associative array containing the structure of the form. + * @param array $form_state + * A reference to a keyed array containing the current state of the form. + */ + public function previous(array $form, array &$form_state) { + $entity = $this->buildEntity($form, $form_state); + // Get the steps by key. + $steps = array_keys($this->entityInfo[$this->stepKey]); + // Get the steps before the current step. + $before = array_slice($this->entityInfo[$this->stepKey], 0, array_search($this->getOperation(), $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 = array_reverse($before); + + $form_state['redirect'] = $this->redirect($entity, $before[0]); + } + + /** + * Form submission handler for the 'previous' action. + * + * Retrieves the entity, saves it and deletes the values in the tempstore. + * This is handled as a separate method from submit() in order to keep the + * concerns of next() separate from finish(). There is no next() method + * because submit() will be fired in all forward moving steps. Submit just + * checks the operator to make sure that it is not firing on finish(). + * + * @param array $form + * An associative array containing the structure of the form. + * @param array $form_state + * A reference to a keyed array containing the current state of the form. + */ + public function finish(array $form, array &$form_state) { + $entity = parent::submit($form, $form_state); + $entity->save(); + drupal_container()->get('user.tempstore')->get($entity->entityType())->delete($entity->id()); + $form_state['redirect'] = $this->redirect($entity); + } + + /** + * A simple method for managing the redirect url of each step. + * + * @param EntityInterface $entity + * The entity being saved or updated. + * @param string $step + * A simple string representation of the current step and corresponding + * form controller. + * + * @return string + * A uri for to be placed in $form_state['redirect']. + */ + public function redirect(EntityInterface $entity, $step = NULL) { + if (!empty($step)) { + return $this->entityInfo[$this->destinationKey] . '/' . $entity->id() . '/' . $step; + } + return $this->entityInfo[$this->destinationKey]; + } +} diff --git a/core/modules/system/system.module b/core/modules/system/system.module index cdf7b90..8b8e661 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1176,6 +1176,41 @@ function system_plugin_ui_access($plugin, $facet = NULL) { return $plugin_ui->access($facet); } + +/** + * Provides a generic wrapper for multi page entity wizard forms. + * + * @param $entity_type + * The type of entity we are working with. This will be used for + * entity_create() calls as well as unique storage within tempstore. + * @param $step + * The current step in the form wizard process. + * @param $id + * The unique identifier of the entity the form wizard is building. + * + * @return array + * The renderable array from the current step of the entity's available form + * controllers. + */ +function system_wizard_form($entity_type, $step, $id = NULL) { + if (empty($id)) { + $entity = entity_create($entity_type, array()); + } + else { + $entity = drupal_container()->get('user.tempstore')->get($entity_type)->get($id); + if (!method_exists($entity, 'entityType')) { + $entity = entity_load($entity_type, $id); + } + } + $definition = $entity->entityInfo(); + // This could be any entity form controller and is not necessarily documented + // in the steps. If it is not in the steps, it will not have a title. + if (isset($definition['steps'][$step])) { + drupal_set_title($definition['steps'][$step]); + } + return entity_get_form($entity, $step); +} + /** * Implements hook_forms(). */