'features_form');
$form['feature'] =
$form['links'] =
$form['version'] =
$form['weight'] =
$form['status'] = array(
'#tree' => TRUE,
);
$modules = features_get_modules('', TRUE);
$conflicts = features_get_conflicts();
// Generate features form.
foreach (features_get_features('', TRUE) as $name => $module) {
$form['feature'][$name] = array(
'#type' => 'value',
'#value' => $module,
);
$disabled = FALSE;
$description = $module->info['description'];
// Detect unmet dependencies
if (!empty($module->info['dependencies'])) {
$unmet_dependencies = array();
$dependencies = _features_export_maximize_dependencies($module->info['dependencies']);
foreach ($dependencies as $dependency) {
if (empty($modules[$dependency])) {
$unmet_dependencies[] = theme('features_module_status', FEATURES_MODULE_MISSING, $dependency);
}
}
if (!empty($unmet_dependencies)) {
$description .= "
". t('Unmet dependencies: !dependencies', array('!dependencies' => implode(', ', $unmet_dependencies))) ."
";
$disabled = TRUE;
}
}
// Detect potential conflicts
if (!empty($conflicts[$name])) {
$module_conflicts = array();
foreach ($conflicts[$name] as $conflict) {
$module_conflicts[] = theme('features_module_status', FEATURES_MODULE_MISSING, $conflict);
// Only disable modules with conflicts if they are not already enabled.
// If they are already enabled, somehow the user got themselves into a
// bad situation and they need to be able to disable a conflicted module.
if (module_exists($conflict) && !module_exists($name)) {
$disabled = TRUE;
}
}
$description .= "". t('Incompatible with: !conflicts', array('!conflicts' => implode(', ', $module_conflicts))) ."
";
}
$form['status'][$name] = array(
'#type' => 'checkbox',
'#title' => $module->info['name'],
'#description' => $description,
'#default_value' => $module->status,
'#disabled' => $disabled,
);
$uri = !empty($module->info['project status url']) ? l(truncate_utf8($module->info['project status url'], 35, TRUE, TRUE), $module->info['project status url']) : t('Unavailable');
$version = !empty($module->info['version']) ? $module->info['version'] : '';
$version = !empty($version) ? "$version
" : '';
$sign = "$uri $version";
$form['sign'][$name] = array(
'#type' => 'markup',
'#value' => $sign,
);
$actions = l(t('View'), "admin/build/features/{$name}");
$actions .= ' | ';
$actions .= l(t('Update'), "admin/build/features/{$name}/update", array(
'attributes' => array('class' => 'admin-update')
));
$actions .= ' | ';
$actions .= l(t('Revert'), "admin/build/features/{$name}/revert", array(
'attributes' => array('class' => 'admin-overridden')
));
if ($module->status) {
$state = '' . t('Checking...') . '';
$state .= l(t('Check'), "admin/build/features/{$name}/status", array('attributes' => array('class' => 'admin-check')));
$state .= l(theme('features_storage', FEATURES_OVERRIDDEN), "admin/build/features/{$name}/compare", array(
'html' => TRUE,
'attributes' => array('class' => 'admin-overridden')
));
$state .= theme('features_storage', FEATURES_DEFAULT);
}
else {
$state = t('Disabled');
}
$form['state'][$name] = array(
'#type' => 'markup',
'#value' => !empty($state) ? $state : '',
);
$form['actions'][$name] = array(
'#type' => 'markup',
'#value' => !empty($actions) ? $actions : '',
);
}
$form['buttons'] = array(
'#theme' => 'features_form_buttons',
);
$form['buttons']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save settings'),
'#submit' => array('features_form_submit'),
'#validate' => array('features_form_validate'),
);
return $form;
}
/**
* Display the components of a feature.
*/
function features_admin_components($feature) {
module_load_include('inc', 'features', 'features.export');
// Iterate over dependencies and retrieve status for display
$dependencies = array();
if (!empty($feature->info['dependencies'])) {
foreach ($feature->info['dependencies'] as $dependency) {
$status = features_get_module_status($dependency);
$dependencies[$dependency] = $status;
}
}
// Iterate over components and retrieve status for display
$components = $feature->info['features'];
$overrides = features_detect_overrides($feature);
$conflicts = array();
foreach ($feature->info['features'] as $component => $items) {
if (!empty($overrides[$component])) {
$conflicts[$component] = l(theme('features_storage', FEATURES_OVERRIDDEN), "admin/build/features/{$feature->name}/compare", array('html' => TRUE));
unset($overrides[$component]);
}
else {
$conflicts[$component] = theme('features_storage', FEATURES_DEFAULT);
}
}
return theme('features_admin_components', $feature->info, $dependencies, $conflicts);
}
/**
* Validate handler for the 'manage features' form.
*/
function features_form_validate(&$form, &$form_state) {
$conflicts = features_get_conflicts();
foreach ($form_state['values']['status'] as $module => $status) {
if ($status && !empty($conflicts[$module])) {
foreach ($conflicts[$module] as $conflict) {
if (!empty($form_state['values']['status'][$conflict])) {
form_set_error('status', t('The feature !module cannot be enabled because it conflicts with !conflict.', array('!module' => $module, '!conflict' => $conflict)));
}
}
}
}
}
/**
* Submit handler for the 'manage features' form
*/
function features_form_submit(&$form, &$form_state) {
module_load_include('inc', 'features', 'features.export');
$features = $form_state['values']['feature'];
$install = array();
$disable = array();
if (!empty($features)) {
foreach ($features as $name => $feature) {
// Enable feature
if ($form_state['values']['status'][$name] && !module_exists($name)) {
if (!empty($feature->info['dependencies'])) {
$dependencies = _features_export_maximize_dependencies($feature->info['dependencies']);
foreach ($dependencies as $dependency) {
if (!module_exists($dependency)) {
$install[] = $dependency;
}
}
}
$install[] = $name;
}
// Disable feature
else if (!$form_state['values']['status'][$name] && module_exists($name)) {
$disable[] = $name;
}
}
if (!empty($install)) {
$install = array_unique($install);
module_enable($install);
// Make sure the install API is available.
include_once './includes/install.inc';
drupal_install_modules($install);
}
if (!empty($disable)) {
module_disable($disable);
}
}
// Clear drupal caches after enabling a feature. We do this in a separate
// page callback rather than as part of the submit handler as some modules
// have includes/other directives of importance in hooks that have already
// been called in this page load.
$form_state['redirect'] = 'admin/build/features/cache-clear';
}
/**
* Clear caches after enabling a feature.
*/
function features_cache_clear() {
drupal_flush_all_caches();
drupal_goto('admin/build/features');
}
/**
* Features revert form.
*/
function features_revert_form($form_state, $feature) {
$form = array(
'module' => array(
'#type' => 'value',
'#value' => $feature->name,
),
'revert' => array(
'#type' => 'checkboxes',
'#title' => t('Revert all components'),
'#options' => array(),
'#default_value' => array(),
),
'submit' => array(
'#type' => 'submit',
'#value' => t('Submit'),
),
);
// Find all overridden items
features_include();
module_load_include('inc', 'features', 'features.export');
$overrides = features_detect_overrides($feature);
foreach (array_keys($overrides) as $component) {
if (features_hook($component, 'features_revert')) {
$form['revert']['#options'][$component] = $component;
$form['revert']['#default_value'][$component] = $component;
}
}
return $form;
}
/**
* Submit handler for revert form.
*/
function features_revert_form_submit(&$form, &$form_state) {
features_include();
$module = $form_state['values']['module'];
foreach ($form_state['values']['revert'] as $component => $status) {
if ($status) {
$result = features_invoke($component, 'features_revert', $module);
if ($result) {
drupal_set_message(t('Reverted all !component components for !module.', array('!component' => $component, '!module' => $module)));
}
else {
drupal_set_message(t('An error occurred trying to revert !component components for !module.', array('!component' => $component, '!module' => $module)));
}
}
}
$form_state['redirect'] = 'admin/build/features/'. $module;
}
/**
* Page callback to display the differences between what's in code and
* what is in the db.
*
* @param $module
* The name of the feature module to check display differences for.
*
* @return Themed display of what is different.
*/
function features_feature_comparison($feature) {
drupal_add_css(drupal_get_path('module', 'features') .'/features.css');
module_load_include('inc', 'features', 'features.export');
$overrides = features_detect_overrides($feature);
// There are differences
if (!empty($overrides)) {
if (module_exists('diff')) {
module_load_include('php', 'diff', 'DiffEngine');
$formatter = new DrupalDiffFormatter();
}
else {
$diff_message = t('Please install !diff to view detailed output.', array('!diff' => l('diff', 'http://drupal.org/project/diff', array('absolute' => TRUE))));
$diff_message = theme('table', array(), array(array($diff_message)));
}
foreach ($overrides as $component => $items) {
if ($component == 'info') {
$output .= "{$component}
";
if (module_exists('diff')) {
$diff = new Diff(explode("\n", $items['default']), explode("\n", $items['current']));
$output .= theme('diff_table', array(t('Default'), '', t('Current'), ''), $formatter->format($diff), array('class' => 'diff features-diff'));
}
else {
$output .= $diff_message;
}
}
else {
foreach ($items as $k => $v) {
$output .= is_numeric($k) ? "{$component}
" : "{$component}: {$k}
";
if (module_exists('diff')) {
$diff = new Diff(explode("\n", $v['default']), explode("\n", $v['current']));
$output .= theme('diff_table', array(t('Default'), '', t('Current'), ''), $formatter->format($diff), array('class' => 'diff features-diff'));
}
else {
$output .= $diff_message;
}
}
}
}
}
// No changes
else {
$output = "". t('No changes have been made to this feature.') ."
";
}
$output = "{$output}
";
return $output;
}
/**
* Javascript call back that returns the status of a feature.
*/
function features_feature_status($feature) {
module_load_include('inc', 'features', 'features.export');
$storage = count(features_detect_overrides($feature)) ? FEATURES_OVERRIDDEN : FEATURES_DEFAULT;
return drupal_json(array('status' => $storage));
}
/**
* Detect potential conflicts between any features that provide
* identical components.
*/
function features_get_conflicts($reset = FALSE) {
$conflicts = array();
$component_info = features_get_components();
$map = features_get_component_map($reset);
foreach ($map as $type => $components) {
foreach ($components as $component => $modules) {
if (isset($component_info[$type]['duplicates']) && $component_info[$type]['duplicates'] == FEATURES_DUPLICATES_ALLOWED) {
continue;
}
else if (count($modules) > 1) {
foreach ($modules as $module) {
if (!isset($conflicts[$module])) {
$conflicts[$module] = array();
}
foreach ($modules as $m) {
if ($m != $module) {
$conflicts[$module][$m] = $m;
}
}
}
}
}
}
return $conflicts;
}
/**
* Provide a component to feature map.
*/
function features_get_component_map($reset = FALSE) {
static $map;
if (!isset($map) || $reset) {
$map = array();
$features = features_get_features('', TRUE);
foreach ($features as $feature) {
foreach ($feature->info['features'] as $type => $components) {
if (!isset($map[$type])) {
$map[$type] = array();
}
foreach ($components as $component) {
$map[$type][$component][] = $feature->name;
}
}
}
}
return $map;
}