'Features',
'description' => 'Administer features.',
'page callback' => 'drupal_get_form',
'page arguments' => array('features_admin_form'),
'type' => MENU_NORMAL_ITEM,
'file' => 'features.admin.inc',
);
$items['admin/build/features/cache-clear'] = array(
'title' => 'Features',
'description' => 'Administer features.',
'page callback' => 'features_cache_clear',
'type' => MENU_CALLBACK,
'file' => 'features.admin.inc',
);
$items['admin/build/features/manage'] = array(
'title' => 'Manage',
'description' => 'Enable and disable features.',
'page callback' => 'drupal_get_form',
'page arguments' => array('features_admin_form'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'file' => 'features.admin.inc',
);
$items['admin/build/features/export'] = array(
'title' => 'Export',
'description' => 'Export features to code.',
'page callback' => 'drupal_get_form',
'page arguments' => array('features_export_form'),
'type' => MENU_LOCAL_TASK,
'file' => "features.export.inc",
'weight' => 10,
);
$items['admin/build/features/%feature'] = array(
'title' => 'Feature components',
'description' => 'Display components of a feature.',
'page callback' => 'features_admin_components',
'page arguments' => array(3),
'type' => MENU_CALLBACK,
'file' => 'features.admin.inc',
);
$items['admin/build/features/%feature/view'] = array(
'title' => 'View',
'description' => 'Display components of a feature.',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['admin/build/features/%feature/compare'] = array(
'title' => 'Compare',
'description' => 'Compare default and current feature.',
'page callback' => 'features_feature_comparison',
'page arguments' => array(3),
'type' => MENU_LOCAL_TASK,
'file' => 'features.admin.inc',
);
$items['admin/build/features/%feature/export'] = array(
'title' => 'Export',
'description' => 'Update a feature.',
'page callback' => 'drupal_get_form',
'page arguments' => array('features_export_form', 3),
'type' => MENU_LOCAL_TASK,
'file' => "features.export.inc",
'weight' => 10,
);
foreach ($items as $path => $item) {
$items[$path]['access callback'] = 'user_access';
$items[$path]['access arguments'] = array('administer site configuration');
}
return $items;
}
/**
* Implementation of hook_menu_alter().
*/
function features_menu_alter(&$cache) {
// Collect exported menu items and graft them into the features menu.
$items = module_invoke_all('menu_default_items');
drupal_alter('menu_default_items', $items);
foreach ($items as $item) {
if (!empty($item['path'])) {
$path = $item['path'];
unset($item['path']);
if (!empty($cache[$path])) {
$cache[$path] = array_merge($cache[$path], $item);
$cache[$path]['type'] = MENU_NORMAL_ITEM;
$cache[$path]['menu_name'] = 'features';
}
}
}
}
/**
* Implementation of hook_theme().
*/
function features_theme() {
$items = array();
$path = drupal_get_path('module', 'features') .'/theme';
$items['features_form'] =
$items['features_export_context_form'] =
$items['features_export_form_confirm'] =
$items['features_export_form_final'] = array('arguments' => array('form' => NULL));
$items['features_form_buttons'] = array(
'arguments' => array('element' => NULL),
'file' => 'theme.inc',
'path' => $path,
);
$items['features_admin_components'] = array(
'arguments' => array('info' => NULL, 'dependencies' => NULL, 'components' => NULL, 'conflicts' => NULL),
'template' => 'features-admin-components',
'file' => 'theme.inc',
'path' => $path,
);
$items['features_module_status'] = array(
'arguments' => array('module' => NULL, 'status' => NULL),
'file' => 'theme.inc',
'path' => $path,
);
$items['features_storage'] = array(
'arguments' => array('storage' => NULL),
'file' => 'theme.inc',
'path' => $path,
);
return $items;
}
/**
* Implementation of hook_theme_registry_alter().
*/
function features_theme_registry_alter(&$theme_registry) {
// Ensures that features_preprocess_page() comes immediately after
// template_preprocess_page().
if ($position = array_search('features_preprocess_page', $theme_registry['page']['preprocess functions'])) {
unset($theme_registry['page']['preprocess functions'][$position]);
}
$position = array_search('template_preprocess_page', $theme_registry['page']['preprocess functions']);
$position = $position ? $position + 1 : 2;
array_splice($theme_registry['page']['preprocess functions'], $position, 0, 'features_preprocess_page');
}
/**
* Implementation of hook_flush_caches().
*/
function features_flush_caches() {
features_rebuild();
return array();
}
/**
* Feature object loader.
*/
function feature_load($name, $reset = FALSE) {
return features_get_features($name, $reset);
}
/**
* Return a module 'object' including .info information.
*
* @param $name
* The name of the module to retrieve information for. If ommitted,
* an array of all available modules will be returned.
* @param $reset
* Whether to reset the cache.
*
* @return
* If a module is request (and exists) a module object is returned. If no
* module is requested info for all modules is returned.
*/
function features_get_modules($name = NULL, $reset = FALSE) {
return features_get_info('module', $name, $reset);
}
function features_get_features($name = NULL, $reset = FALSE) {
return features_get_info('feature', $name, $reset);
}
function features_get_info($type = 'module', $name = NULL, $reset = FALSE) {
$cache = cache_get('features_module_info');
if (($cache == 0) || $reset) {
$data = array();
$result = db_query("SELECT filename, name, type, status, throttle, schema_version FROM {system} WHERE type = 'module' ORDER BY name ASC");
while ($row = db_fetch_object($result)) {
$row->info = drupal_parse_info_file(dirname($row->filename) .'/'. $row->name .'.info');
if (!empty($row->info)) {
if (!empty($row->info['features'])) {
$data['feature'][$row->name] = $row;
}
$data['module'][$row->name] = $row;
}
}
cache_set("features_module_info", $data);
$cache = new stdClass();
$cache->data = $data;
}
if (!empty($name)) {
return !empty($cache->data[$type][$name]) ? $cache->data[$type][$name] : array();
}
return !empty($cache->data[$type]) ? $cache->data[$type] : array();
}
/**
* Simple wrapper returns the status of a module.
*/
function features_get_module_status($module) {
if (module_exists($module)) {
return FEATURES_MODULE_ENABLED;
}
else if (features_get_modules($module)) {
return FEATURES_MODULE_DISABLED;
}
else {
return FEATURES_MODULE_MISSING;
}
}
/**
* Implementation of hook_form_alter() for features_admin_form.
*/
function features_form_features_admin_form_alter(&$form) {
features_rebuild();
}
/**
* Implementation of hook_form_alter() for system_modules form.
*/
function features_form_system_modules_alter(&$form) {
features_rebuild();
// Hide feature module from admin/build/modules
foreach ($form['validation_modules']['#value'] as $name => $module) {
if (isset($module->info['features'])) {
unset($form['validation_modules']['#value'][$name]);
foreach (array('name', 'version', 'description') as $key) {
unset($form[$key][$name]);
}
unset($form['status']['#options'][$name]);
}
}
}
/**
* Rebuild definitions -- calls various scripts for non-exportables.
*/
function features_rebuild() {
if (module_exists('content')) {
features_content_fields_rebuild();
}
}
/**
* Rebuild CCK field definitions.
*/
function features_content_fields_rebuild() {
module_load_include('inc', 'content', 'includes/content.crud');
content_clear_type_cache(TRUE);
$fields = module_invoke_all('content_default_fields');
foreach ($fields as $field) {
$conflicts = array();
$existing_field = content_fields($field['field_name']);
$existing_instance = content_fields($field['field_name'], $field['type_name']);
// If field storage doesn't exists, create it
if (!$existing_field) {
content_field_instance_create($field);
}
// If field storage exists, check that storage and key descriptors don't conflict
else {
// Iterate through field properties to determine compatibility
foreach ($field as $key => $value) {
$excluded = array('type_name');
if (!is_array($value) && !in_array($key, $excluded)) {
if ($value !== $existing_field[$key]) {
$conflicts[] = $key;
}
}
}
if (!empty($conflicts)) {
$tokens = array('!field' => $field['field_name'], '!type' => $field['type_name'], '!list' => implode(', ', $conflicts));
$message = t('Field !field was not created for !type because a conflict was detected for the following properties: !list', $tokens);
drupal_set_message($message);
}
else if (!$existing_instance) {
content_field_instance_create($field);
}
}
// If there are no field conflicts, update the type instance
if (empty($conflicts)) {
$instance = content_fields($field['field_name'], $field['type_name']);
if (!empty($instance)) {
$field = array_merge($instance, $field);
}
content_field_instance_update($field);
}
else {
drupal_set_message(t('Field !field was not updated for !type because conflicts were detected.', array('!field' => $field['field_name'], '!type' => $field['type_name'])));
}
}
}
/**
* Returns a links array in the theme_links() format for the features menu.
*
* @return
* Array of links.
*/
function features_menu_links($level = 0, $reset = FALSE) {
static $links;
if (!isset($links[$level]) || $reset) {
if (!isset($links)) {
$links = array();
}
$links[$level] = menu_navigation_links('features', $level);
}
drupal_alter('features_menu_links', $links[$level]);
return $links[$level];
}
/**
* Export var function -- from Views.
*/
function features_var_export($var, $prefix = '', $init = TRUE) {
if (is_array($var)) {
if (empty($var)) {
$output = 'array()';
}
else {
$output = "array(\n";
foreach ($var as $key => $value) {
$output .= " '$key' => " . features_var_export($value, ' ', FALSE) . ",\n";
}
$output .= ')';
}
}
else if (is_bool($var)) {
$output = $var ? 'TRUE' : 'FALSE';
}
else if (is_string($var) && strpos($var, "\n") !== FALSE) {
// Replace line breaks in strings with a token for replacement
// at the very end. This protects whitespace in strings from
// unintentional indentation.
$var = str_replace("\n", "***BREAK***", $var);
$output = var_export($var, TRUE);
}
else {
$output = var_export($var, TRUE);
}
if ($prefix) {
$output = str_replace("\n", "\n$prefix", $output);
}
if ($init) {
$output = str_replace("***BREAK***", "\n", $output);
}
return $output;
}
/**
* Helper function to eliminate whitespace differences in code.
*/
function _features_linetrim($code) {
foreach ($code as $k => $line) {
$code[$k] = trim($line);
}
return $code;
}
/**
* Theme functions ====================================================
*/
/**
* Preprocess function for features links.
*/
function features_preprocess_page(&$vars) {
if (variable_get('menu_primary_links_source', 'primary-links') == 'features') {
$vars['primary_links'] = features_menu_links();
}
if (variable_get('menu_secondary_links_source', 'secondary-links') == 'features') {
if (variable_get('menu_secondary_links_source', 'primary-links') == 'features') {
$vars['secondary_links'] = features_menu_links(1);
}
else {
$vars['secondary_links'] = features_menu_links();
}
}
}