protected function FeaturesEditForm::getComponentList in Features 8.3
Same name and namespace in other branches
- 8.4 modules/features_ui/src/Form/FeaturesEditForm.php \Drupal\features_ui\Form\FeaturesEditForm::getComponentList()
Returns the full feature export array based upon user selections in form_state.
Parameters
\Drupal\Core\Form\FormStateInterface $form_state: Optional form_state information for user selections. Can be updated to reflect new selection status.
Return value
\Drupal\features\Package New export array to be exported array['components'][$component_name] = $component_info $component_info['_features_options'][$section] is list of available options $component_info['_features_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
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
2 calls to FeaturesEditForm::getComponentList()
- FeaturesEditForm::buildComponentList in modules/
features_ui/ src/ Form/ FeaturesEditForm.php - Returns the render array elements for the Components selection on the Edit form.
- FeaturesEditForm::updatePackageConfig in modules/
features_ui/ src/ Form/ FeaturesEditForm.php - Updates the config stored in the package from the current edit form.
File
- modules/
features_ui/ src/ Form/ FeaturesEditForm.php, line 657
Class
- FeaturesEditForm
- Defines the features settings form.
Namespace
Drupal\features_ui\FormCode
protected function getComponentList(FormStateInterface $form_state) {
$config = $this->featuresManager
->getConfigCollection();
$package_name = $this->package
->getMachineName();
// Auto-detect dependencies for included config.
$package_config = $this->package
->getConfig();
if (!empty($this->package
->getConfigOrig())) {
$package_config = array_unique(array_merge($package_config, $this->package
->getConfigOrig()));
}
if (!empty($package_config)) {
$this->featuresManager
->assignConfigDependents($package_config, $package_name);
}
$packages = $this->featuresManager
->getPackages();
// Re-fetch the package in case config was updated with Dependents above.
$this->package = $packages[$package_name];
// Make a map of all config data.
$components = [];
$this->conflicts = [];
foreach ($config as $item_name => $item) {
if ($item
->getPackage() != $package_name && !empty($packages[$item
->getPackage()]) && $packages[$item
->getPackage()]
->getStatus() != FeaturesManagerInterface::STATUS_NO_EXPORT) {
$this->conflicts[$item
->getType()][$item
->getShortName()] = $item
->getLabel();
}
if ($this->allowConflicts || !isset($this->conflicts[$item
->getType()][$item
->getShortName()]) || $this->package
->getConfigOrig() && in_array($item_name, $this->package
->getConfigOrig())) {
$components[$item
->getType()][$item
->getShortName()] = $item
->getLabel();
}
}
// Make a map of the config data already exported to the Feature.
$this->missing = [];
$exported_features_info = [];
foreach ($this->package
->getConfigOrig() as $item_name) {
// Make sure the extension provided item exists in the active
// configuration storage.
if (isset($config[$item_name])) {
$item = $config[$item_name];
// Remove any conflicts if those are not being allowed.
// if ($this->allowConflicts || !isset($this->conflicts[$item['type']][$item['name_short']])) {
$exported_features_info[$item
->getType()][$item
->getShortName()] = $item
->getLabel();
// }
}
else {
$this->missing[] = $item_name;
}
}
$exported_features_info['dependencies'] = $this->package
->getDependencyInfo();
// Make a map of any config specifically excluded and/or required.
foreach ([
'excluded',
'required',
] as $constraint) {
$this->{$constraint} = [];
$info = !empty($this->package
->{'get' . $constraint}()) ? $this->package
->{'get' . $constraint}() : [];
// $info may be boolean.
if (is_array($info)) {
foreach ($info as $item_name) {
if (!isset($config[$item_name])) {
continue;
}
$item = $config[$item_name];
$this->{$constraint}[$item
->getType()][$item
->getShortName()] = $item
->getLabel();
}
}
}
// Make a map of the config data to be exported within the Feature.
$new_features_info = [];
foreach ($this->package
->getConfig() as $item_name) {
$item = $config[$item_name];
$new_features_info[$item
->getType()][$item
->getShortName()] = $item
->getLabel();
}
$new_features_info['dependencies'] = $this->package
->getDependencies();
// Assemble the combined component list.
$config_new = [];
$sections = [
'sources',
'included',
'detected',
'added',
];
// Generate list of config to be exported.
$config_count = [];
foreach ($components as $component => $component_info) {
// User-selected components take precedence.
$config_new[$component] = [];
$config_count[$component] = 0;
// Add selected items from Sources checkboxes.
if (!$form_state
->isValueEmpty([
$component,
'sources',
'selected',
])) {
// Don't use the array_merge function, otherwise configs like
// "metatag.metatag_defaults.404" will have the key "404" be reindexed.
$config_new[$component] = $config_new[$component] + $this
->domDecodeOptions(array_filter($form_state
->getValue([
$component,
'sources',
'selected',
])));
$config_count[$component]++;
}
// Add selected items from already Included, newly Added, auto-detected
// checkboxes.
foreach ([
'included',
'added',
'detected',
] as $section) {
if (!$form_state
->isValueEmpty([
$component,
$section,
])) {
$config_new[$component] = $config_new[$component] + $this
->domDecodeOptions(array_filter($form_state
->getValue([
$component,
$section,
])));
$config_count[$component]++;
}
}
// Only fallback to an existing feature's values if there are no export
// options for the component.
if ($component == 'dependencies') {
if ($config_count[$component] == 0 && !empty($exported_features_info['dependencies'])) {
$config_new[$component] = array_combine($exported_features_info['dependencies'], $exported_features_info['dependencies']);
}
}
elseif ($config_count[$component] == 0 && !empty($exported_features_info[$component])) {
$config_names = array_keys($exported_features_info[$component]);
$config_new[$component] = array_combine($config_names, $config_names);
}
}
// Generate new populated feature.
$export['package'] = $this->package;
$export['config_new'] = $config_new;
// 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['_features_options'][$section] = [];
$component_export['_features_selected'][$section] = [];
}
if (!empty($component_info)) {
$exported_components = !empty($exported_features_info[$component]) ? $exported_features_info[$component] : [];
$new_components = !empty($new_features_info[$component]) ? $new_features_info[$component] : [];
foreach ($component_info as $key => $label) {
$config_name = $this->featuresManager
->getFullName($component, $key);
// If checkbox in Sources is checked, move it to Added section.
if (!$form_state
->isValueEmpty([
$component,
'sources',
'selected',
$key,
])) {
$form_state
->setValue([
$component,
'sources',
'selected',
$key,
], FALSE);
$form_state
->setValue([
$component,
'added',
$key,
], 1);
$component_export['_features_options']['added'][$key] = $this
->configLabel($component, $key, $label);
$component_export['_features_selected']['added'][$key] = $key;
// If this was previously excluded, we don't need to set it as
// required because it was automatically assigned.
if (isset($this->excluded[$component][$key])) {
unset($this->excluded[$component][$key]);
}
else {
$this->required[$component][$key] = $key;
}
}
elseif (isset($new_components[$key]) || isset($config_new[$component][$key])) {
// Option is in the New exported array.
if (isset($exported_components[$key])) {
// Option was already previously exported so it's part of the
// Included checkboxes.
$section = 'included';
$default_value = $key;
// If Included item was un-selected (removed from export
// $config_new) but was re-detected in the $new_components
// means it was an auto-detect that was previously part of the
// export and is now de-selected in UI.
if ($form_state
->isSubmitted() && ($form_state
->hasValue([
$component,
'included',
$key,
]) || $form_state
->isValueEmpty([
$component,
'detected',
$key,
])) && empty($config_new[$component][$key])) {
$section = 'detected';
$default_value = FALSE;
}
elseif ($form_state
->isSubmitted() && $form_state
->isValueEmpty([
$component,
'added',
$key,
]) && $form_state
->isValueEmpty([
$component,
'detected',
$key,
]) && $form_state
->isValueEmpty([
$component,
'included',
$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';
$default_value = NULL;
// Check for item explicitly excluded.
if (isset($this->excluded[$component][$key]) && !$form_state
->isSubmitted()) {
$default_value = FALSE;
}
else {
$default_value = $key;
}
// If it's already checked in Added or Sources, leave it in Added
// as checked.
if ($form_state
->isSubmitted() && (!$form_state
->isValueEmpty([
$component,
'added',
$key,
]) || !$form_state
->isValueEmpty([
$component,
'sources',
'selected',
$key,
]))) {
$section = 'added';
$default_value = $key;
}
elseif ($form_state
->isSubmitted() && $form_state
->isValueEmpty([
$component,
'sources',
'selected',
$key,
]) && $form_state
->isValueEmpty([
$component,
'detected',
$key,
]) && !$form_state
->hasValue([
$component,
'added',
$key,
])) {
$section = 'detected';
$default_value = FALSE;
}
}
$component_export['_features_options'][$section][$key] = $this
->configLabel($component, $key, $label);
$component_export['_features_selected'][$section][$key] = $default_value;
// Save which dependencies are specifically excluded from
// auto-detection.
if ($section == 'detected' && $default_value === FALSE) {
// If this was previously required, we don't need to set it as
// excluded because it wasn't automatically assigned.
if (!isset($this->required[$component][$key]) || $this->package
->getRequired() === TRUE) {
$this->excluded[$component][$key] = $key;
}
unset($this->required[$component][$key]);
// Remove excluded item from export.
if ($component == 'dependencies') {
$export['package']
->removeDependency($key);
}
else {
$export['package']
->removeConfig($config_name);
}
}
else {
unset($this->excluded[$component][$key]);
}
// Remove the 'input' and set the 'values' so Drupal stops looking
// at 'input'.
if ($form_state
->isSubmitted()) {
if (!$default_value) {
$form_state
->setValue([
$component,
$section,
$key,
], FALSE);
}
else {
$form_state
->setValue([
$component,
$section,
$key,
], 1);
}
}
}
elseif (!$form_state
->isSubmitted() && isset($exported_components[$key])) {
// Component is not part of new export, but was in original export.
// Mark component as Added when creating initial form.
$component_export['_features_options']['added'][$key] = $this
->configLabel($component, $key, $label);
$component_export['_features_selected']['added'][$key] = $key;
}
else {
// Option was not part of the new export.
$added = FALSE;
foreach ([
'included',
'added',
] as $section) {
// Restore any user-selected checkboxes.
if (!$form_state
->isValueEmpty([
$component,
$section,
$key,
])) {
$component_export['_features_options'][$section][$key] = $this
->configLabel($component, $key, $label);
$component_export['_features_selected'][$section][$key] = $key;
$added = TRUE;
}
}
if (!$added) {
// If not Included or Added, then put it back in the unchecked
// Sources checkboxes.
$component_export['_features_options']['sources'][$key] = $this
->configLabel($component, $key, $label);
$component_export['_features_selected']['sources'][$key] = FALSE;
}
}
}
}
$export['components'][$component] = $component_export;
}
$export['features_exclude'] = $this->excluded;
$export['features_require'] = $this->required;
$export['conflicts'] = $this->conflicts;
$export['missing'] = $this->missing;
return $export;
}