You are here

function _features_export_build in Features 7.2

Return the full feature export array based upon user selections in form_state.

NOTE: This routine gets a bit complex to handle all of the different possible user checkbox selections and de-selections. Cases to test: 1a) uncheck Included item -> mark as Added but unchecked 1b) re-check unchecked Added item -> return it to Included check item 2a) check Sources item -> mark as Added and checked 2b) uncheck Added item -> return it to Sources as unchecked 3a) uncheck Included item that still exists as auto-detect -> mark as Detected but unchecked 3b) re-check Detected item -> return it to Included and checked 4a) check Sources item should also add any auto-detect items as Detected and checked 4b) uncheck Sources item with auto-detect and auto-detect items should return to Sources and unchecked 5a) uncheck a Detected item -> refreshing page should keep it as unchecked Detected 6) when nothing changes, refresh should not change any state 7) should never see an unchecked Included item.

Parameters

\stdClass $feature: Feature info object.

array $form_state: Optional form_state information for user selections. Can be updated to reflect new selection status.

Return value

array New export array to be exported array['components'][$component_name] = $component_info $component_info['options'][$section] is list of available options $component_info['selected'][$section] is option state TRUE/FALSE $section = array('sources', included', 'detected', 'added') sources - options that are available to be added to the feature included - options that have been previously exported to the feature detected - options that have been auto-detected added - newly added options to the feature

2 calls to _features_export_build()
features_export_build_form_submit in ./features.admin.inc
First submit handler 'Generate feature' and 'Download feature' buttons.
_features_export_form_components in ./features.admin.inc
Adds form elements for component selection on the export form.

File

./features.admin.inc, line 566
Forms for Features admin screens.

Code

function _features_export_build($feature, &$form_state) {
  global $features_ignore_conflicts;

  // Set a global to effect features_get_component_map when building feature
  // hate to use a global, but it's just for an admin screen so probably ok.
  if (isset($_SESSION['features_allow_conflicts'])) {
    $features_ignore_conflicts = $_SESSION['features_allow_conflicts'];
  }
  $feature_name = isset($feature->name) ? $feature->name : NULL;
  $conflicts = _features_get_used($feature_name);
  $reset = FALSE;
  if (isset($form_state['triggering_element']['#name']) && $form_state['triggering_element']['#name'] == 'features_allow_conflicts') {

    // When the 'Allow conflicts to be added' checkbox gets checked, reset the
    // feature back to its original state.
    $reset = TRUE;
  }
  module_load_include('inc', 'features', 'features.export');
  features_include();
  $components = features_get_components();
  uasort($components, 'features_compare_component_name');

  // Assemble the combined component list.
  $stub = array();
  $sections = array(
    'sources',
    'included',
    'detected',
    'added',
  );

  // Create a new feature "stub" to populate.
  $stub_count = array();
  foreach ($components as $component => $component_info) {
    if ($reset) {
      unset($form_state['values'][$component]);
    }
    if (!variable_get('features_admin_show_component_' . $component, TRUE)) {
      unset($components[$component]);
      continue;
    }

    // User-selected components take precedence.
    $stub[$component] = array();
    $stub_count[$component] = 0;

    // Add selected items from 'sources' checkboxes.
    if (!empty($form_state['values'][$component]['sources']['selected'])) {
      $stub[$component] = array_merge($stub[$component], features_dom_decode_options(array_filter($form_state['values'][$component]['sources']['selected'])));
      $stub_count[$component]++;
    }

    // Add selected items from already included and newly added checkboxes.
    foreach (array(
      'included',
      'added',
    ) as $section) {
      if (!empty($form_state['values'][$component][$section])) {
        $stub[$component] = array_merge($stub[$component], features_dom_decode_options(array_filter($form_state['values'][$component][$section])));
        $stub_count[$component]++;
      }
    }

    // Count any detected items.
    if (!empty($form_state['values'][$component]['detected'])) {
      $stub_count[$component]++;
    }

    // Only fallback to an existing feature's values if there are no export
    // options for the component.
    if ($component == 'dependencies') {
      if ($stub_count[$component] == 0 && !empty($feature->info['dependencies'])) {
        $stub[$component] = drupal_map_assoc($feature->info['dependencies']);
      }
    }
    elseif ($stub_count[$component] == 0 && !empty($feature->info['features'][$component])) {
      $stub[$component] = drupal_map_assoc($feature->info['features'][$component]);
    }
  }

  // Generate new populated feature.
  $export = features_populate(array(
    'features' => $stub,
    'dependencies' => $stub['dependencies'],
  ), $feature_name);

  // Components that are already exported to feature.
  $exported_features_info = !empty($feature->info['features']) ? $feature->info['features'] : array();
  $exported_features_info['dependencies'] = !empty($feature->info['dependencies']) ? $feature->info['dependencies'] : array();

  // Components that should be exported.
  $new_features_info = !empty($export['features']) ? $export['features'] : array();
  $new_features_info['dependencies'] = !empty($export['dependencies']) ? $export['dependencies'] : array();
  $excluded = !empty($feature->info['features_exclude']) ? $feature->info['features_exclude'] : array();

  // Now fill the $export with categorized sections of component options
  // based upon user selections and de-selections.
  foreach ($components as $component => $component_info) {
    $component_export = $component_info;
    foreach ($sections as $section) {
      $component_export['options'][$section] = array();
      $component_export['selected'][$section] = array();
    }

    /* @see \hook_features_export_options() */
    $options = features_invoke($component, 'features_export_options');

    /* @see \hook_features_export_options_alter() */
    drupal_alter('features_export_options', $options, $component);
    if (!empty($options)) {
      $exported_components = !empty($exported_features_info[$component]) ? $exported_features_info[$component] : array();
      $new_components = !empty($new_features_info[$component]) ? $new_features_info[$component] : array();

      // Find all default components that are not provided by this feature and
      // strip them out of the possible options.
      if ($map = features_get_default_map($component)) {
        foreach ($map as $k => $v) {
          if (isset($options[$k]) && (!isset($feature->name) || $v !== $feature->name)) {
            unset($options[$k]);
          }
        }
      }
      foreach ($options as $key => $value) {

        // Use the $clean_key when accessing $form_state.
        $clean_key = features_dom_encode($key);

        // If checkbox in 'sources' is checked, move it to the 'added' section.
        if (!empty($form_state['values'][$component]['sources']['selected'][$clean_key])) {
          unset($form_state['input'][$component]['sources']['selected'][$clean_key]);
          $form_state['values'][$component]['sources']['selected'][$clean_key] = FALSE;
          $form_state['values'][$component]['added'][$clean_key] = 1;
          $form_state['input'][$component]['added'][$clean_key] = $clean_key;
          $component_export['options']['added'][$key] = check_plain($value);
          $component_export['selected']['added'][$key] = $key;
        }
        elseif (in_array($key, $new_components)) {

          // Option is in the New exported array.
          if (in_array($key, $exported_components)) {

            // Option was already previously exported
            // so it's part of the Included checkboxes.
            $section = 'included';
            $default_value = $key;
            if ($reset) {

              // Leave it included.
            }
            elseif (!empty($form_state['values']) && (isset($form_state['values'][$component]['included'][$clean_key]) || empty($form_state['values'][$component]['detected'][$clean_key])) && empty($stub[$component][$key])) {
              $section = 'detected';
              $default_value = FALSE;
            }
            elseif (!empty($form_state['values']) && empty($form_state['values'][$component]['added'][$clean_key]) && empty($form_state['values'][$component]['detected'][$clean_key]) && empty($form_state['values'][$component]['included'][$clean_key])) {
              $section = 'added';
              $default_value = FALSE;
            }
          }
          else {

            // Option was in New exported array, but NOT in already exported
            // so it's a user-selected or an auto-detect item.
            $section = 'detected';

            // Check for item explicity excluded.
            if (isset($excluded[$component][$key]) && !isset($form_state['values'][$component]['detected'][$clean_key])) {
              $default_value = FALSE;
            }
            else {
              $default_value = $key;
            }

            // If it's already checked in Added or Sources, leave it in Added as checked.
            if (!empty($form_state['values']) && (!empty($form_state['values'][$component]['added'][$clean_key]) || !empty($form_state['values'][$component]['sources']['selected'][$clean_key]))) {
              $section = 'added';
              $default_value = $key;
            }
            elseif (!empty($form_state['values']) && empty($form_state['values'][$component]['sources']['selected'][$clean_key]) && empty($form_state['values'][$component]['detected'][$clean_key]) && !isset($form_state['values'][$component]['added'][$clean_key])) {
              $section = 'detected';
              $default_value = FALSE;
            }
          }
          $component_export['options'][$section][$key] = check_plain($value);
          $component_export['selected'][$section][$key] = $default_value;

          // Save which dependencies are specifically excluded from auto-detection.
          if ($section == 'detected' && $default_value === FALSE) {
            $excluded[$component][$key] = $key;

            // Remove excluded item from export.
            if ($component == 'dependencies') {
              unset($export['dependencies'][$key]);
            }
            else {
              unset($export['features'][$component][$key]);
            }
          }
          else {
            unset($excluded[$component][$key]);
          }

          // Remove the 'input' and set the 'values' so Drupal stops looking at 'input'.
          if (isset($form_state['values'])) {
            if (!$default_value) {
              unset($form_state['input'][$component][$section][$clean_key]);
              $form_state['values'][$component][$section][$clean_key] = FALSE;
            }
            else {
              $form_state['input'][$component][$section][$clean_key] = $clean_key;
              $form_state['values'][$component][$section][$clean_key] = 1;
            }
          }
        }
        else {

          // Option was not part of the new export.
          $added = FALSE;
          foreach (array(
            'included',
            'added',
          ) as $section) {

            // Restore any user-selected checkboxes.
            if (!empty($form_state['values'][$component][$section][$clean_key])) {
              $component_export['options'][$section][$key] = check_plain($value);
              $component_export['selected'][$section][$key] = $key;
              $added = TRUE;
            }
          }
          if (!$added) {

            // If not Included or Added, then put it back in the unchecked Sources checkboxes.
            $component_export['options']['sources'][$key] = check_plain($value);
            $component_export['selected']['sources'][$key] = FALSE;
          }
        }
      }
    }
    $export['components'][$component] = $component_export;
  }
  $export['features_exclude'] = $excluded;

  // Make excluded list and conflicts available for javascript to pass to our ajax callback.
  drupal_add_js(array(
    'features' => array(
      'excluded' => $excluded,
      'conflicts' => $conflicts,
    ),
  ), 'setting');
  return $export;
}