$info) { // Add any extenders. // @todo: consider allowing extender classes to declare dependencies on // other extender classes, to ensure they work in the correct order? if (isset($info['wizard extenders'])) { foreach ($info['wizard extenders'] as $wizard_class => $extender_classes) { // Note that $class_name is in lower case, so we can't just use isset() // to find our wizard. if (strtolower($wizard_class) == $class_name) { foreach ($extender_classes as $extender_class) { $wizard->addExtender($extender_class); } } } } } $form_state['wizard'] = $wizard; } else { $wizard = $form_state['wizard']; } // Fetch the form for the wizard's current step. $form = $wizard->form($form_state); return $form; } /** * Submit handler for the "previous" button. Moves the wizard back to the * previous step, and retrieves the values that were submitted on that step. * * @todo: Can we remove steps that were dynamically added? */ function migrate_ui_wizard_previous_submit($form, &$form_state) { /** @var MigrateUIWizard $wizard */ $wizard = $form_state['wizard']; $wizard->gotoPreviousStep($form_state); } /** * Validate handler for the 'next' button. Dispatches to the wizard's current * step for validation. */ function migrate_ui_wizard_next_validate($form, &$form_state) { /** @var MigrateUIWizard $wizard */ $wizard = $form_state['wizard']; $wizard->formValidate($form_state); } /** * Submit handler for the 'next' button. Saves the form values for the step * we're leaving, so Previous can pick them up, and moves the wizard to the * next step. */ function migrate_ui_wizard_next_submit($form, &$form_state) { /** @var MigrateUIWizard $wizard */ $wizard = $form_state['wizard']; $wizard->gotoNextStep($form_state); } /** * Submit handler for the Save settings button. Register the migrations that were * (implicitly) defined along the way and redirect to the Migrate dashboard. */ function migrate_ui_wizard_submit($form, &$form_state) { /** @var MigrateUIWizard $wizard */ $wizard = $form_state['wizard']; $wizard->formSaveSettings(); $form_state['redirect'] = 'admin/content/migrate/groups/' . $wizard->getGroupName(); } /** * Submit handler for the "Save settings and import" button. Register the * migrations that were (implicitly) defined along the way, run the import, and * redirect to the Migrate dashboard. */ function migrate_ui_wizard_migrate_submit($form, &$form_state) { /** @var MigrateUIWizard $wizard */ $wizard = $form_state['wizard']; $wizard->formSaveSettings(); $wizard->formPerformImport(); $form_state['redirect'] = 'admin/content/migrate/groups/' . $wizard->getGroupName(); } /** * The base class for migration wizards. Extend this class to implement a * wizard UI for importing into Drupal from a given source format (Drupal, * WordPress, etc.). */ abstract class MigrateUIWizard { /** * We maintain a doubly-linked list of wizard steps, both to support * previous/next, and to easily insert steps dynamically. * * The first step of the wizard, which has no predecessor. Will generally be * an overview/introductory page. * * @var MigrateUIStep */ protected $firstStep; /** * The last step of the wizard, which has no successor. Will generally be a * review page. * * @var MigrateUIStep */ protected $lastStep; /** * Get the list of steps currently defined. * * @return * An array of MigrateUIStep objects, in the order defined, keyed by the step * name. */ protected function getSteps() { $steps = array(); $steps[$this->firstStep->getName()] = $this->firstStep; $next_step = $this->firstStep->nextStep; while (!is_null($next_step)) { $steps[$next_step->getName()] = $next_step; $next_step = $next_step->nextStep; } return $steps; } /** * The current step of the wizard (the one being shown in the UI, and the one * whose button is being clicked on). * * @var MigrateUIStep */ protected $currentStep; /** * The step number, used in the page title. * * @var int */ protected $stepNumber = 1; /** * The group name to assign to any Migration instances created. * * @var string */ protected $groupName = 'default'; public function getGroupName() { return $this->groupName; } /** * The user-visible title of the group. * * @var string */ protected $groupTitle = 'default'; /** * Any arguments that apply to all migrations in the group. * * @var array */ protected $groupArguments = array(); /** * Array of Migration argument arrays, keyed by machine name. On Finish, used * to register Migrations. * * @var array */ protected $migrations = array(); /** * Array of MigrateUIWizardExtender objects that extend this wizard. * * @var array */ protected $extenders = array(); public function getExtender($extender_class) { if (isset($this->extenders[$extender_class])) { return $this->extenders[$extender_class]; } else { return NULL; } } /** * Returns the translatable name representing the source of the data (e.g., * "Drupal", "WordPress", etc.). * * @return string */ abstract public function getSourceName(); public function __construct() {} /** * Add a wizard extender. * * This initializes the new extender and adds it to our internal list. * * @param $extender_class * The name of an extender class. */ public function addExtender($extender_class) { $steps = $this->getSteps(); $extender = new $extender_class($this, $steps); $this->extenders[$extender_class] = $extender; } /** * Add a step to the wizard, using a step name and method. * * @param string $name * Translatable name for the step, to be used in the page title. * @param callable $form_method * Callable returning the form array for the step. This can be either the * name of a MigrateUIWizard method, or a callable array specifying a method * on a wizard extender. The validation method is formed from the method's * name with the suffix 'Validate' added. * @param MigrateUIStep $after * Optional step after which to insert the new step. If omitted, add it at * the end. * @param mixed $context * Optional data to be used by this step's form. * * @return MigrateUIStep */ public function addStep($name, $form_method, MigrateUIStep $after = NULL, $context = NULL) { if (!is_array($form_method)) { $form_method = array($this, $form_method); } $new_step = new MigrateUIStep($name, $form_method, $context); // There were no steps, so this is the only one. if (is_null($this->firstStep)) { $this->firstStep = $this->lastStep = $this->currentStep = $new_step; } else { // If no insertion point is specified, append to the end. if (is_null($after)) { $after = $this->lastStep; } // Do the insert, rewriting the links appropriately. $new_step->nextStep = $after->nextStep; if (is_null($new_step->nextStep)) { $this->lastStep = $new_step; } else { $new_step->nextStep->previousStep = $new_step; } $new_step->previousStep = $after; $after->nextStep = $new_step; } return $new_step; } /** * Remove the named step from the wizard. * * @param $name */ protected function removeStep($name) { for ($current_step = $this->firstStep; !is_null($current_step); $current_step = $current_step->nextStep) { if ($current_step->getName() == $name) { if (is_null($current_step->previousStep)) { $this->firstStep = $current_step->nextStep; } else { $current_step->previousStep->nextStep = $current_step->nextStep; } if (is_null($current_step->nextStep)) { $this->lastStep = $current_step->previousStep; } else { $current_step->nextStep->previousStep = $current_step->previousStep; } break; } } } /** * Move the wizard to the next step in line (if any), first squirreling away * the current step's form values. */ public function gotoNextStep(&$form_state) { if ($this->currentStep && $this->currentStep->nextStep) { $this->currentStep->setFormValues($form_state['values']); $form_state['rebuild'] = TRUE; $this->currentStep = $this->currentStep->nextStep; $this->stepNumber++; // Ensure a page reload remains on the current step. $current_step_form_values = $this->currentStep->getFormValues(); if (!empty($current_step_form_values)) { $form_state['values'] = $current_step_form_values; } else { $form_state['values'] = array(); } } } /** * Move the wizard to the previous step in line (if any), restoring its * form values. */ public function gotoPreviousStep(&$form_state) { if ($this->currentStep && $this->currentStep->previousStep) { $this->currentStep = $this->currentStep->previousStep; $this->stepNumber--; $form_state['values'] = $this->currentStep->getFormValues(); $form_state['rebuild'] = TRUE; } } /** * Build the form for the current step. * * @return array */ public function form(&$form_state) { drupal_set_title(t('Import from @source_title', array('@source_title' => $this->getSourceName()))); $form_method = $this->currentStep->getFormMethod(); $form['title'] = array( '#prefix' => '