diff --git a/features.admin.inc b/features.admin.inc
index 4e12576..48235e5 100644
--- a/features.admin.inc
+++ b/features.admin.inc
@@ -1095,8 +1095,13 @@ function features_admin_form($form, $form_state) {
);
// Add in recreate link
+ $markup = l(t('Recreate'), "admin/structure/features/{$name}/recreate", array('attributes' => array('class' => array('admin-update'))));
+ if (features_get_module_status($name) == FEATURES_MODULE_ENABLED) {
+ // If module is not disabled, add in consolidate link
+ $markup .= ' ' . l(t('Consolidate'), "admin/structure/features/{$name}/consolidate", array('attributes' => array('class' => array('admin-update'))));
+ }
$form[$package]['actions'][$name] = array(
- '#markup' => l(t('Recreate'), "admin/structure/features/{$name}/recreate", array('attributes' => array('class' => array('admin-update')))),
+ '#markup' => $markup,
);
}
}
@@ -1228,7 +1233,11 @@ function features_admin_components_revert(&$form, &$form_state) {
features_include();
$module = $form_state['values']['module'];
$revert = array($module => array());
- foreach (array_filter($form_state['values']['revert']) as $component => $status) {
+ $components = array_filter($form_state['values']['revert']);
+ if ($components == array()) {
+ $components = $form_state['values']['revert'];
+ }
+ foreach ($components as $component => $status) {
$revert[$module][] = $component;
drupal_set_message(t('Reverted all @component components for @module.', array('@component' => $component, '@module' => $module)));
}
@@ -1319,6 +1328,22 @@ function features_cleanup_form($form, $form_state, $cache_clear = FALSE) {
}
/**
+ * Page callback to consolidate a feature back into the database.
+ *
+ * @param $feature
+ * The loaded feature object to be consolidated.
+ */
+function features_feature_consolidate($feature) {
+ $destination = $_SERVER['HTTP_REFERER'];
+ features_include();
+ $module = $feature->name;
+ foreach ($feature->info['features'] as $component => $items) {
+ features_invoke($component, 'features_consolidate', $items, $module);
+ }
+ drupal_goto($destination);
+}
+
+/**
* Page callback to display the differences between what's in code and
* what is in the db.
*
diff --git a/features.api.php b/features.api.php
index 10915e1..81f0c5f 100644
--- a/features.api.php
+++ b/features.api.php
@@ -239,6 +239,18 @@ function hook_features_export_alter(&$export, $module_name) {
}
/**
+ * TODO
+ */
+function hook_features_consolidate($data, &$export, $module_name) {
+ // The following is the simplest implementation of a straight object export
+ // with no further export processors called.
+ foreach ($data as $component) {
+ $export['mycomponent'][$component] = $component;
+ }
+ return array();
+}
+
+/**
* Alter the pipe array for a given component. This hook should be implemented
* with the name of the component type in place of `component` in the function
* name, e.g. `features_pipe_views_alter()` will alter the pipe for the Views
diff --git a/features.drush.inc b/features.drush.inc
index 9da547d..af69994 100644
--- a/features.drush.inc
+++ b/features.drush.inc
@@ -75,6 +75,19 @@ function features_drush_command() {
),
'aliases' => array('fc'),
);
+ $items['features-consolidate'] = array(
+ 'description' => "Consolidate a feature to the database.",
+ 'drupal dependencies' => array('features'),
+ 'aliases' => array('fco'),
+ );
+ $items['features-consolidate-all'] = array(
+ 'description' => "Consolidate all features to the database.",
+ 'drupal dependencies' => array('features'),
+ 'options' => array(
+ 'exclude' => "A comma-separated list of features to exclude from consolidate.",
+ ),
+ 'aliases' => array('fco-all', 'fcoa'),
+ );
$items['features-update'] = array(
'description' => "Update a feature module on your site.",
'arguments' => array(
@@ -166,6 +179,10 @@ Patterns uses * or % for matching multiple sources/components. Unlike shorthands
Lastly, a pattern without a colon is interpreted as having \":%\" appended, for easy listing of all components of a source.
");
+ case 'drush:features-consolidate':
+ return dt("Consolidate a feature to the database.");
+ case 'drush:features-consolidate-all':
+ return dt("Consolidate all features to the database.");
case 'drush:features-update':
return dt("Update a feature module on your site. The option '--version-set=foo' may be used to specify a version number for the feature or the option '--version-increment' may also to increment the feature's version number.");
case 'drush:features-update-all':
@@ -499,6 +516,58 @@ function drush_features_add() {
/**
+ * Consolidate a feature to the database.
+ */
+function drush_features_consolidate() {
+ if ($args = func_get_args()) {
+ foreach ($args as $module) {
+ if (($feature = feature_load($module, TRUE)) && module_exists($module)) {
+ drush_log(dt("Consolidating features for module !module", array('!module' => $module)), 'notice');
+ _drush_features_consolidate($feature->info['features'], $module);
+ }
+ elseif ($feature) {
+ _features_drush_set_error($module, 'FEATURES_FEATURE_NOT_ENABLED');
+ }
+ else {
+ _features_drush_set_error($module);
+ }
+ }
+ }
+ else {
+ // By default just show contexts that are available.
+ $rows = array(array(dt('Available features')));
+ foreach (features_get_features(NULL, TRUE) as $name => $info) {
+ $rows[] = array($name);
+ }
+ drush_print_table($rows, TRUE);
+ }
+}
+
+/**
+ * Consolidate all features to the database.
+ */
+function drush_features_consolidate_all() {
+ $features_to_consolidate = array();
+ $features_to_exclude = _convert_csv_to_array(drush_get_option('exclude'));
+
+ $features = features_get_features();
+ foreach ($features as $module) {
+ if ($module->status && !in_array($module->name, $features_to_exclude)) {
+ $features_to_consolidate[] = $module->name;
+ }
+ }
+ drush_print(dt('The following modules will be consolidated: !modules', array('!modules' => implode(', ', $features_to_consolidate))));
+ if (drush_confirm(dt('Do you really want to continue?'))) {
+ foreach ($features_to_consolidate as $module_name) {
+ drush_invoke_process('@self', 'features-consolidate', array($module_name));
+ }
+ }
+ else {
+ return drush_user_abort();
+ }
+}
+
+/**
* Update an existing feature module.
*/
function drush_features_update() {
@@ -654,6 +723,17 @@ function _drush_features_generate_export(&$info, &$module_name) {
}
/**
+ * Consolidate a feature to the database.
+ */
+function _drush_features_consolidate($features = array(), $module_name = '') {
+ features_include();
+ foreach ($features as $component => $items) {
+ drush_log(dt("Running consolidate for component !component", array('!component' => $component)), 'notice');
+ features_invoke($component, 'features_consolidate', $items, $module_name);
+ }
+}
+
+/**
* Revert a feature to it's code definition.
* Optionally accept a list of components to revert.
*/
diff --git a/features.module b/features.module
index ba5b9fe..a591ad5 100644
--- a/features.module
+++ b/features.module
@@ -154,6 +154,18 @@ function features_menu() {
'file' => "features.admin.inc",
'weight' => 11,
);
+ $items['admin/structure/features/%feature/consolidate'] = array(
+ 'title' => 'Consolidate',
+ 'description' => 'Consolidate a feature to the database.',
+ 'page callback' => 'features_feature_consolidate',
+ 'page arguments' => array(3),
+ 'load arguments' => array(3, TRUE),
+ 'access callback' => 'features_access_consolidate_feature',
+ 'access arguments' => array(3),
+ 'type' => MENU_LOCAL_TASK,
+ 'file' => "features.admin.inc",
+ 'weight' => 12,
+ );
if (module_exists('diff')) {
$items['admin/structure/features/%feature/diff'] = array(
'title' => 'Review overrides',
@@ -876,6 +888,14 @@ function features_access_override_actions($feature) {
}
/**
+ * Menu access callback for whether a user should be able to
+ * consolidate a given feature.
+ */
+function features_access_consolidate_feature($feature) {
+ return (user_access('administer features') && (features_get_module_status($feature->name) == FEATURES_MODULE_ENABLED));
+}
+
+/**
* Implements hook_form_alter() for system_modules form().
*/
function features_form_system_modules_alter(&$form) {
diff --git a/includes/features.context.inc b/includes/features.context.inc
index 2da59a7..dd6b937 100644
--- a/includes/features.context.inc
+++ b/includes/features.context.inc
@@ -52,3 +52,15 @@ function context_features_revert($module = NULL) {
context_invalidate_cache();
return $return;
}
+
+
+/**
+ * Implementation of hook_features_consolidate().
+ */
+function context_features_consolidate($items = array(), $module_name = '') {
+ foreach ($items as $context_name) {
+ if ($context = context_load($context_name)) {
+ context_save($context);
+ }
+ }
+}
diff --git a/includes/features.image.inc b/includes/features.image.inc
index 2b5eb27..d92197e 100644
--- a/includes/features.image.inc
+++ b/includes/features.image.inc
@@ -99,3 +99,36 @@ function _image_features_style_sanitize(&$style, $child = FALSE) {
}
}
}
+
+/**
+ * Implementation of hook_features_consolidate().
+ */
+function image_features_consolidate($items = array(), $module_name = '') {
+ foreach ($items as $style_name) {
+ $style = image_style_load($style_name);
+ // Styles
+ if (isset($style['isid'])) {
+ if (!is_numeric($style['isid'])) {
+ _image_style_and_effects_save($style);
+ }
+ }
+ else {
+ _image_style_and_effects_save($style);
+ }
+ }
+}
+
+/**
+ * Helper function for image_features_consolidate().
+ * Saves a single style together with its effects.
+ */
+function _image_style_and_effects_save($style) {
+ $style = image_style_save($style);
+ // Actions
+ if (is_numeric($style['isid'])) {
+ foreach ($style['effects'] as $effect) {
+ $effect['isid'] = $style['isid'];
+ image_effect_save($effect);
+ }
+ }
+}
diff --git a/includes/features.node.inc b/includes/features.node.inc
index 7beb55f..f9df80c 100644
--- a/includes/features.node.inc
+++ b/includes/features.node.inc
@@ -115,7 +115,7 @@ function node_features_revert($module = NULL) {
}
/**
- * Implements hook_features_disable().
+ * Implements hook_features_disable_feature().
*
* When a features module is disabled, modify any node types it provides so
* they can be deleted manually through the content types UI.
@@ -123,7 +123,7 @@ function node_features_revert($module = NULL) {
* @param $module
* Name of module that has been disabled.
*/
-function node_features_disable($module) {
+function node_features_disable_feature($module) {
if ($default_types = features_get_default('node', $module)) {
foreach ($default_types as $type_name => $type_info) {
$type_info = node_type_load($type_name);
@@ -137,7 +137,7 @@ function node_features_disable($module) {
}
/**
- * Implements hook_features_enable().
+ * Implements hook_features_enable_feature().
*
* When a features module is enabled, modify any node types it provides so
* they can no longer be deleted manually through the content types UI.
@@ -145,7 +145,7 @@ function node_features_disable($module) {
* @param $module
* Name of module that has been enabled.
*/
-function node_features_enable($module) {
+function node_features_enable_feature($module) {
if ($default_types = features_get_default('node', $module)) {
foreach ($default_types as $type_name => $type_info) {
// Ensure the type exists.
@@ -159,3 +159,28 @@ function node_features_enable($module) {
}
}
}
+
+/**
+ * Implementation of hook_features_consolidate().
+ *
+ * When a features module is consolidated, modify any node types it provides
+ * so responsibility is delegated to the node module.
+ *
+ * @param $items
+ * Array of names of node types to be consolidated.
+ * @param $module
+ * Name of module that has been consolidated.
+ */
+function node_features_consolidate($items = array(), $module = 'feature') {
+ module_load_include('inc', 'features', 'features.export');
+ // Delegate responsibility to node module
+ foreach ($items as $type_name) {
+ // Load the type.
+ $type_info = node_type_load($type_name);
+ $type_info->module = 'node';
+ $type_info->custom = 1;
+ $type_info->modified = 1;
+ $type_info->locked = 0;
+ node_type_save($type_info);
+ }
+}