You are here

protected function D7Webform::buildFormElements in Webform: Migrate 8

Same name and namespace in other branches
  1. 8.2 src/Plugin/migrate/source/d7/D7Webform.php \Drupal\webform_migrate\Plugin\migrate\source\d7\D7Webform::buildFormElements()

Build form elements from webform component table.

1 call to D7Webform::buildFormElements()
D7Webform::prepareRow in src/Plugin/migrate/source/d7/D7Webform.php
Adds additional data to the row.

File

src/Plugin/migrate/source/d7/D7Webform.php, line 184

Class

D7Webform
Drupal 7 webform source from database.

Namespace

Drupal\webform_migrate\Plugin\migrate\source\d7

Code

protected function buildFormElements($nid) {

  // TODO : Use yaml_emit http://php.net/manual/en/function.yaml-emit.php
  $output = '';
  $query = $this
    ->select('webform_component', 'wc');
  $query
    ->fields('wc', [
    'nid',
    'cid',
    'pid',
    'form_key',
    'name',
    'type',
    'value',
    'extra',
    'required',
    'weight',
  ]);
  $components = $query
    ->condition('nid', $nid)
    ->orderBy('pid')
    ->orderBy('weight')
    ->execute();
  $children = [];
  $parents = [];
  $elements = [];
  $xref = [];

  // Build an array of elements in the correct order for rendering based on
  // pid and weight and a cross reference array to match cid with form_key
  // used by email handler.
  $multiPage = FALSE;
  foreach ($components as $component) {
    $xref[$component['cid']] = $component['form_key'];
    if ($component['type'] == 'pagebreak') {

      // Pagebreak found so we have a multi-page form.
      $multiPage = TRUE;
    }
    $children[$component['pid']][] = $component['cid'];
    $parents[$component['cid']][] = $component['pid'];
    $elements[$component['cid']] = $component;
  }

  // Keeps track of the parents we have to process, the last entry is used
  // for the next processing step.
  $process_parents = [];
  $process_parents[] = 0;
  $elements_tree = [];

  // Loops over the parent components and adds its children to the tree array.
  // Uses a loop instead of a recursion, because it's more efficient.
  while (count($process_parents)) {
    $parent = array_pop($process_parents);

    // The number of parents determines the current depth.
    $depth = count($process_parents);
    if (!empty($children[$parent])) {
      $has_children = FALSE;
      $child = current($children[$parent]);
      do {
        if (empty($child)) {
          break;
        }
        $element =& $elements[$child];
        $element['depth'] = $depth;

        // We might get element with same form_key
        // d8 doesn't like that so rename it.
        if ($depth > 0) {
          $element['form_key'] = $element['form_key'] . '_' . $element['pid'];
        }
        unset($element['pid']);
        $elements_tree[] = $element;
        if (!empty($children[$element['cid']])) {
          $has_children = TRUE;

          // We have to continue with this parent later.
          $process_parents[] = $parent;

          // Use the current component as parent for the next iteration.
          $process_parents[] = $element['cid'];

          // Reset pointers for child lists because we step in there more often
          // with multi parents.
          reset($children[$element['cid']]);

          // Move pointer so that we get the correct term the next time.
          next($children[$parent]);
          break;
        }
      } while ($child = next($children[$parent]));
      if (!$has_children) {

        // We processed all components in this hierarchy-level.
        reset($children[$parent]);
      }
    }
  }

  // If form has multiple pages then start first page automatically.
  if ($multiPage) {
    $pageCnt = 1;
    $current_page = 'wizard_page_1';
    $output .= "first_page:\n  '#type': webform_wizard_page\n  '#title': {" . $current_page . "_title}\n";
    $current_page_title = 'Start';
  }
  foreach ($elements_tree as $element) {

    // Rename fieldsets to it's own unique key.
    if ($element['type'] == 'fieldset' && strpos($element['form_key'], 'fieldset') === FALSE) {
      $element['form_key'] = 'fieldset_' . $element['form_key'];
    }

    // If this is a multi-page form then indent all elements one level
    // to allow for page elements.
    if ($multiPage && $element['type'] != 'pagebreak') {
      $element['depth'] += 1;
    }
    $indent = str_repeat(' ', $element['depth'] * 2);
    $extra = unserialize($element['extra']);

    // The description key can be missing (since description is optional and
    // it isn't saved by Drupal 7 webform when it is left empty).
    $description = !empty($extra['description']) ? $this
      ->cleanString($extra['description']) : NULL;

    // Create an option list if there are items for this element.
    $options = '';
    $valid_options = [];
    if (!empty($extra['items'])) {
      $items = explode("\n", trim($extra['items']));
      $ingroup = '';
      foreach ($items as $item) {
        $item = trim($item);
        if (!empty($item)) {

          // Handle option groups.
          if (preg_match('/^<(.*)>$/', $item, $matches)) {
            if (empty(trim($matches[1]))) {
              $ingroup = '';
              continue;
            }
            $options .= "{$indent}    '" . $matches[1] . "':\n";
            $ingroup = str_repeat(' ', 2);
          }
          else {
            $option = explode('|', $item);
            $valid_options[] = $option[0];
            if (count($option) == 2) {
              $options .= "{$indent}{$ingroup}    " . $option[0] . ": '" . str_replace('\'', '"', $option[1]) . "'\n";
            }
            else {
              $options .= "{$indent}{$ingroup}    " . $option[0] . ": '" . str_replace('\'', '"', $option[0]) . "'\n";
            }
          }
        }
      }
    }

    // Replace any tokens in the value.
    if (!empty($element['value'])) {
      $element['value'] = $this
        ->replaceTokens($element['value']);
    }
    $markup = $indent . strtolower($element['form_key']) . ":\n";
    switch ($element['type']) {
      case 'fieldset':
        if ($multiPage && empty($current_page_title)) {
          $current_page_title = $element['name'];
        }
        $markup .= "{$indent}  '#type': fieldset\n{$indent}  '#open': true\n";
        break;
      case 'textfield':
        $markup .= "{$indent}  '#type': textfield\n";
        if (!empty($extra['width'])) {
          $markup .= "{$indent}  '#size': " . (int) $extra['width'] . "\n";
        }
        break;
      case 'textarea':
        $markup .= "{$indent}  '#type': textarea\n";
        break;
      case 'select':
        if (!empty($extra['aslist'])) {
          $select_type = 'select';
        }
        elseif (!empty($extra['multiple']) && count($valid_options) > 1) {
          $select_type = 'checkboxes';
        }
        elseif (!empty($extra['multiple']) && count($valid_options) == 1) {
          $select_type = 'checkbox';
          list($key, $desc) = explode('|', $extra['items']);
          $markup .= "{$indent}  '#description': \"" . $this
            ->cleanString($desc) . "\"\n";
        }
        else {
          $select_type = 'radios';
        }
        $markup .= "{$indent}  '#type': {$select_type}\n";
        $markup .= "{$indent}  '#options':\n" . $options;
        if (!empty($extra['multiple'])) {
          $markup .= "{$indent}  '#multiple': true\n";
        }
        break;
      case 'email':
        $markup .= "{$indent}  '#type': email\n{$indent}  '#size': 20\n";
        break;
      case 'number':
        if ($extra['type'] == 'textfield') {
          $markup .= "{$indent}  '#type': textfield\n{$indent}  '#size': 20\n";
        }
        elseif ($extra['type'] == 'select') {
          $markup .= "{$indent}  '#type': select\n";
          $markup .= "{$indent}  '#options':\n" . $options;
          $min = $extra['min'];
          $max = $extra['max'];
          $step = !empty($extra['step']) ? $extra['step'] : 1;
          for ($value = $min; $value <= $max; $value += $step) {
            $markup .= "{$indent}    " . $value . ": " . $value . "\n";
          }
        }
        if (!empty($extra['min'])) {
          $markup .= "{$indent}  '#min': " . $extra['min'] . "\n";
        }
        if (!empty($extra['max'])) {
          $markup .= "{$indent}  '#max': " . $extra['max'] . "\n";
        }
        if (!empty($extra['step'])) {
          $markup .= "{$indent}  '#step': " . $extra['step'] . "\n";
        }
        if (isset($extra['unique'])) {
          $unique = $extra['unique'] ? 'true' : 'false';
          $markup .= "{$indent}  '#unique': " . $unique . "\n";
        }
        break;
      case 'markup':
        $markup .= "{$indent}  '#type': processed_text\n{$indent}  '#format': full_html\n{$indent}  '#text': \"" . $this
          ->cleanString($element['value']) . "\"\n";
        $element['value'] = '';
        break;
      case 'file':
      case 'multiple_file':
        $exts = '';
        if (!empty($extra['filtering']['types'])) {
          $types = $extra['filtering']['types'];
          if (!empty($extra['filtering']['addextensions'])) {
            $add_types = explode(',', $extra['filtering']['addextensions']);
            $types = array_unique(array_merge($types, array_map('trim', $add_types)));
          }
          $exts = implode(' ', $types);
        }
        $file_size = '';
        if (!empty($extra['filtering']['size'])) {

          // Get the string for the size. Will be something like "2 MB".
          $size = $extra['filtering']['size'];

          // Convert the string into an integer in bytes.
          $file_size_bytes = Bytes::toInt($size);

          // Convert that to MB.
          $file_size = floor($file_size_bytes / 1024 / 1024);

          // Failsafe as Webform doesn't let you go less than 1MB.
          $file_size = $file_size < 1 ? 1 : $file_size;
        }
        $markup .= "{$indent}  '#type': managed_file\n";
        $markup .= "{$indent}  '#max_filesize': '{$file_size}'\n";
        $markup .= "{$indent}  '#file_extensions': '{$exts}'\n";
        if (!empty($extra['width'])) {
          $markup .= "{$indent}  '#size': " . $extra['width'] . "\n";
        }
        if ($element['type'] == 'multiple_file') {
          $markup .= "{$indent}  '#multiple': true\n";
        }
        break;
      case 'date':
        $markup .= "{$indent}  '#type': date\n";

        /*if (!empty($element['value'])) {
          $element['value'] = date('Y-m-d', strtotime($element['value']));
          }*/
        break;
      case 'time':
        $markup .= "{$indent}  '#type': webform_time\n";
        if (!empty($extra['hourformat'])) {
          if ($extra['hourformat'] == '12-hour') {
            $markup .= "{$indent}  '#time_format': 'g:i A'\n";
          }
          elseif ($extra['hourformat'] == '24-hour') {
            $markup .= "{$indent}  '#time_format': 'H:i'\n";
          }
        }
        if (!empty($extra['minuteincrements'])) {

          // Setting expects seconds not minutes.
          $step = (int) $extra['minuteincrements'] * 60;
          $markup .= "{$indent}  '#step': {$step}\n";
        }

        /*if (!empty($element['value'])) {
          $element['value'] = date('c', strtotime($element['value']));
          }*/
        break;
      case 'hidden':
        $markup .= "{$indent}  '#type': hidden\n";
        break;
      case 'pagebreak':
        $output = str_replace('{' . $current_page . '_title}', $current_page_title, $output);
        $current_page = $element['form_key'];
        $markup .= "{$indent}  '#type': webform_wizard_page\n  '#title': {" . $current_page . "_title}\n";
        $current_page_title = $element['name'];
        $pageCnt++;
        break;
      case 'addressfield':
        $markup .= "{$indent}  '#type': webform_address\n";
        $markup .= "{$indent}  '#state_province__type': textfield\n";
        break;
      case 'grid':
        $questionsArray = $this
          ->getItemsArray($extra['questions']);
        $questions = $this
          ->buildItemsString($questionsArray, $indent . '  ');
        $answersArray = $this
          ->getItemsArray($extra['options']);
        $answers = $this
          ->buildItemsString($answersArray, $indent . '  ');
        $markup .= "{$indent}  '#type': webform_likert\n";
        $markup .= "{$indent}  '#questions':\n" . $questions . "\n";
        $markup .= "{$indent}  '#answers':\n" . $answers . "\n";
        break;
      default:
        echo '';
    }
    if (!empty($element['type']) && is_string($element['type'])) {
      $this
        ->getModuleHandler()
        ->alter('webform_migrate_d7_webform_element_' . $element['type'], $markup, $indent, $element);
    }

    // Add common fields.
    if (!empty(trim($element['value'])) && (empty($valid_options) || in_array($element['value'], $valid_options))) {
      $markup .= "{$indent}  '#default_value': '" . str_replace(array(
        '\'',
        "\n",
        "\r",
      ), array(
        '"',
        '\\n',
        '',
      ), trim($element['value'])) . "'\n";
    }
    if (!empty($extra['field_prefix'])) {
      $markup .= "{$indent}  '#field_prefix': " . $extra['field_prefix'] . "\n";
    }
    if (!empty($extra['field_suffix'])) {
      $markup .= "{$indent}  '#field_suffix': " . $extra['field_suffix'] . "\n";
    }
    if (!empty($extra['title_display']) && $extra['title_display'] != 'before') {
      $title_display = $extra['title_display'];
      if ($title_display == 'none') {
        $title_display = 'invisible';
      }
      $markup .= "{$indent}  '#title_display': " . $title_display . "\n";
    }
    if ($element['type'] != 'pagebreak') {
      $markup .= "{$indent}  '#title': '" . str_replace('\'', '"', $element['name']) . "'\n";
      $markup .= "{$indent}  '#description': \"" . $description . "\"\n";
    }
    if (!empty($element['required'])) {
      $markup .= "{$indent}  '#required': true\n";
    }

    // Build contionals.
    if ($states = $this
      ->buildConditionals($element, $elements)) {
      $markup .= "{$indent}  '#states':\n";
      foreach ($states as $key => $values) {
        $markup .= "{$indent}    {$key}:\n";
        foreach ($values as $value) {
          foreach ($value as $name => $item) {
            $markup .= "{$indent}      " . Yaml::dump($name, 2, 2) . ":\n";
            foreach (explode("\n", Yaml::dump($item, 2, 2)) as $line) {
              if (!empty($line)) {
                $markup .= "{$indent}        " . $line . "\n";
              }
            }
          }
        }
      }
    }
    $output .= $markup;
  }
  if ($multiPage) {

    // Replace the final page title.
    $output = str_replace('{' . $current_page . '_title}', $current_page_title, $output);
  }
  return [
    'elements' => $output,
    'xref' => $xref,
  ];
}