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;
}