commit c8df8dd85b8045881cabec4216788b3bc9a7aa8a Author: Roman Zimmermann Date: Fri Sep 30 19:57:22 2016 +0200 Features vs. module defaults. diff --git a/features.admin.inc b/features.admin.inc index 2dea423..94be1a9 100644 --- a/features.admin.inc +++ b/features.admin.inc @@ -608,13 +608,12 @@ function _features_export_build($feature, &$form_state) { $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 + // Find all default components that are provided by other features 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]); - } + $map = features_get_conflict_map($component); + foreach ($map as $k => $v) { + if ($v && (!isset($feature->name) || !in_array($feature->name, $v))) { + unset($options[$k]); } } foreach ($options as $key => $value) { diff --git a/features.export.inc b/features.export.inc index 5045b13..4ba8cb5 100644 --- a/features.export.inc +++ b/features.export.inc @@ -843,6 +843,34 @@ function features_get_default($component, $module_name = NULL, $alter = TRUE, $r } /** + * Get a map of components to their providing features. + * + * @param string $component_type + * Get the conflict map for all components of this type. + * + * @return string[][] + * An associative keyed by $component names. Each value is an array of + * enabled modules that define this component. + */ +function features_get_conflict_map($component_type) { + if (!empty($GLOBALS['features_ignore_conflict'])) { + return array(); + } + + $map = &drupal_static(__FUNCTION__, array()); + if (!isset($map[$component_type])) { + $component_map = array(); + foreach (features_get_component_map($component_type) as $component => $modules) { + if ($modules = array_filter($modules, 'module_exists')) { + $component_map[$component] = $modules; + } + } + $map[$component_type] = $component_map; + } + return $map[$component_type]; +} + +/** * Get a map of components to their providing modules. * * @param string $component diff --git a/features.module b/features.module index 14fc3f9..1ecbf27 100644 --- a/features.module +++ b/features.module @@ -1093,6 +1093,48 @@ function features_hook_info() { } /** + * Implements hook_ctools_plugin_api_alter(). + * + * Force features default hooks to be executed after non-feature default hooks. + * So features can override module-provided defaults. + */ +function features_ctools_plugin_api_alter(&$modules, $owner, $api) { + // A datastructure for finding feature components based on $owner and $api. + features_include(); + $components = _ctools_features_get_info(); + $apis = array(); + foreach ($components as $name => $c) { + // When writing this there was no information about when the 'api' is set or + // has to be set. For now we only make this work for schemas with it. + if (isset($c['api'])) { + $c['component'] = $name; + $apis[$c['module']][$c['api']] = $c; + } + } + + if (isset($apis[$owner][$api])) { + // Split $modules into features and non-features based on whether the + // matching features component is declared in the modules .info file. + $component = $apis[$owner][$api]['component']; + $module_data = features_get_info(); + + $new_modules = array(); + $features = array(); + + foreach ($modules as $name => $plugin) { + if (!empty($module_data[$name]->components) && in_array($component, $module_data[$name]->components)) { + $features[$name] = $plugin; + } + else { + $new_modules[$name] = $plugin; + } + } + // Non-features will be executed before features. + $modules = array_merge($new_modules, $features); + } +} + +/** * Change vocabularies permission, from vocab id to machine name and vice versa. */ function _user_features_change_term_permission(&$perm, $type = 'vid') { diff --git a/includes/features.ctools.inc b/includes/features.ctools.inc index 387cece..c2d24da 100644 --- a/includes/features.ctools.inc +++ b/includes/features.ctools.inc @@ -155,14 +155,18 @@ function ctools_component_features_export($component, $data, &$export, $module_n if ($module_name !== $info[$component]['module']) { $export['dependencies'][$info[$component]['module']] = $info[$component]['module']; } + $map = features_get_conflict_map($component); // Add the components foreach ($data as $object_name) { if ($object = _ctools_features_export_crud_load($component, $object_name)) { - // If this object is provided as a default by a different module, don't + // If this object is provided as a default by a different feature, don't // export and add that module as a dependency instead. - if (!empty($object->export_module) && $object->export_module !== $module_name) { - $export['dependencies'][$object->export_module] = $object->export_module; + $other_features = isset($map[$object_name]) ? $map[$object_name] : array(); + if ($other_features && !in_array($module_name, $other_features)) { + // Choose the first conflicting feature as dependency. + $other_feature = reset($other_features); + $export['dependencies'][$other_feature] = $other_feature; if (isset($export['features'][$component][$object_name])) { unset($export['features'][$component][$object_name]); }