'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); } } } } /** * 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(); } } }