Skip to content
system.admin.inc 83 KiB
Newer Older
 * @file
 * Admin page callbacks for the system module.
 */

/**
 * Menu callback; Provide the administration overview page.
 */
function system_main_admin_page($arg = NULL) {
  // If we received an argument, they probably meant some other page.
  // Let's 404 them since the menu system cannot be told we do not
  // accept arguments.
  if (isset($arg) && substr($arg, 0, 3) != 'by-') {
    return drupal_not_found();
  }

  // Check for status report errors.
  if (system_status(TRUE) && user_access('administer site configuration')) {
    drupal_set_message(t('One or more problems were detected with your Drupal installation. Check the <a href="@status">status report</a> for more information.', array('@status' => url('admin/reports/status'))), 'error');
  if ($admin = db_fetch_array(db_query("SELECT menu_name, mlid FROM {menu_links} WHERE link_path = 'admin' AND module = 'system'"))) {
    $result = db_query("
      SELECT m.*, ml.*
      FROM {menu_links} ml
      INNER JOIN {menu_router} m ON ml.router_path = m.path
      WHERE ml.link_path != 'admin/help' AND menu_name = '%s' AND ml.plid = %d AND hidden = 0", $admin);
    while ($item = db_fetch_array($result)) {
      _menu_link_translate($item);
      if (!$item['access']) {
        continue;
      }
      // The link 'description' either derived from the hook_menu 'description'
      // or entered by the user via menu module is saved as the title attribute.
      if (!empty($item['localized_options']['attributes']['title'])) {
        $item['description'] = $item['localized_options']['attributes']['title'];
      }
      $block = $item;
      $block['content'] = '';
      if ($item['block_callback'] && function_exists($item['block_callback'])) {
        $function = $item['block_callback'];
        $block['content'] .= $function();
      }
      $block['content'] .= theme('admin_block_content', system_admin_menu_block($item));
      // Prepare for sorting as in function _menu_tree_check_access().
      // The weight is offset so it is always positive, with a uniform 5-digits.
      $blocks[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' . $item['mlid']] = $block;
  if ($blocks) {
    ksort($blocks);
    return theme('admin_page', $blocks);
  }
  else {
    return t('You do not have any administrative items.');
  }
}


/**
 * Provide a single block from the administration menu as a page.
 * This function is often a destination for these blocks.
 * For example, 'admin/build/types' needs to have a destination to be valid
 * in the Drupal menu system, but too much information there might be
 * hidden, so we supply the contents of the block.
 */
function system_admin_menu_block_page() {
  $item = menu_get_item();
  if ($content = system_admin_menu_block($item)) {
    $output = theme('admin_block_content', $content);
  }
  else {
    $output = t('You do not have any administrative items.');
  }

/**
 * Menu callback; prints a listing of admin tasks for each installed module.
 */
function system_admin_by_module() {

  $modules = module_rebuild_cache();
  $menu_items = array();
  $help_arg = module_exists('help') ? drupal_help_arg() : FALSE;

  foreach ($modules as $file) {
    $module = $file->name;
    if ($module == 'help') {
      continue;
    }

    $admin_tasks = system_get_module_admin_tasks($module);

    // Only display a section if there are any available tasks.
    if (count($admin_tasks)) {

      // Check for help links.
      if ($help_arg && module_invoke($module, 'help', "admin/help#$module", $help_arg)) {
        $admin_tasks[100] = l(t('Get help'), "admin/help/$module");
      }

      // Sort.
      ksort($admin_tasks);

      $menu_items[$file->info['name']] = array($file->info['description'], $admin_tasks);
    }
  }
  return theme('system_admin_by_module', $menu_items);
}

/**
 * Menu callback; displays a module's settings page.
 */
function system_settings_overview() {
  // Check database setup if necessary
  if (function_exists('db_check_setup') && empty($_POST)) {
    db_check_setup();
  }

  $item = menu_get_item('admin/settings');
  $content = system_admin_menu_block($item);

  $output = theme('admin_block_content', $content);

  return $output;
}

/**
 * Form builder; This function allows selection of the theme to show in administration sections.
 */
function system_admin_theme_settings() {
  $themes = system_theme_data();

  uasort($themes, 'system_sort_modules_by_info_name');

  $options[0] = '<' . t('System default') . '>';
  foreach ($themes as $theme) {
    $options[$theme->name] = $theme->info['name'];
  }

  $form['admin_theme'] = array(
    '#type' => 'select',
    '#options' => $options,
    '#title' => t('Administration theme'),
    '#description' => t('Choose which theme the administration pages should display in. If you choose "System default" the administration pages will use the same theme as the rest of the site.'),
    '#default_value' => variable_get('admin_theme', '0'),
  );

  $form['node_admin_theme'] = array(
    '#type' => 'checkbox',
    '#title' => t('Use administration theme for content editing'),
    '#description' => t('Use the administration theme when editing existing posts or creating new ones.'),
    '#default_value' => variable_get('node_admin_theme', '0'),
  );

  $form['#submit'][] = 'system_admin_theme_submit';
  return system_settings_form($form);
}

/**
 * Menu callback; displays a listing of all themes.
 */
function system_themes_form() {

  drupal_clear_css_cache();
  $themes = system_theme_data();

  uasort($themes, 'system_sort_modules_by_info_name');

  $status = array();
  $incompatible_core = array();
  $incompatible_php = array();

  foreach ($themes as $theme) {
    $screenshot = NULL;
    $theme_key = $theme->name;
    while ($theme_key) {
      if (file_exists($themes[$theme_key]->info['screenshot'])) {
        $screenshot = $themes[$theme_key]->info['screenshot'];
        break;
      }
      $theme_key = isset($themes[$theme_key]->info['base theme']) ? $themes[$theme_key]->info['base theme'] : NULL;
    }
    $screenshot = $screenshot ? theme('image', $screenshot, t('Screenshot for %theme theme', array('%theme' => $theme->info['name'])), '', array('class' => 'screenshot'), FALSE) : t('no screenshot');

    $form[$theme->name]['screenshot'] = array('#markup' => $screenshot);
    $form[$theme->name]['info'] = array(
      '#type' => 'value',
      '#value' => $theme->info,
    );
    $options[$theme->name] = '';

    if (!empty($theme->status) || $theme->name == variable_get('admin_theme', '0')) {
      $form[$theme->name]['operations'] = array('#markup' => l(t('configure'), 'admin/build/themes/settings/' . $theme->name) );
    }
    else {
      // Dummy element for drupal_render. Cleaner than adding a check in the theme function.
      $form[$theme->name]['operations'] = array();
    }
    if (!empty($theme->status)) {
      $status[] = $theme->name;
    }
    else {
      // Ensure this theme is compatible with this version of core.
      if (!isset($theme->info['core']) || $theme->info['core'] != DRUPAL_CORE_COMPATIBILITY) {
        $incompatible_core[] = $theme->name;
      }
      if (version_compare(phpversion(), $theme->info['php']) < 0) {
        $incompatible_php[$theme->name] = $theme->info['php'];
      }
    }
  }

  $form['status'] = array(
    '#type' => 'checkboxes',
    '#options' => $options,
    '#default_value' => $status,
    '#incompatible_themes_core' => drupal_map_assoc($incompatible_core),
    '#incompatible_themes_php' => $incompatible_php,
  );
  $form['theme_default'] = array(
    '#type' => 'radios',
    '#options' => $options,
    '#default_value' => variable_get('theme_default', 'garland'),
  );
  $form['buttons']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save configuration'),
  );
  $form['buttons']['reset'] = array(
    '#type' => 'submit',
    '#value' => t('Reset to defaults'),
  );
  return $form;
}

/**
 * Process system_themes_form form submissions.
 */
function system_themes_form_submit($form, &$form_state) {

  // Store list of previously enabled themes and disable all themes
  $old_theme_list = $new_theme_list = array();
  foreach (list_themes() as $theme) {
    if ($theme->status) {
      $old_theme_list[] = $theme->name;
    }
  }
  db_query("UPDATE {system} SET status = 0 WHERE type = 'theme'");

  if ($form_state['values']['op'] == t('Save configuration')) {
    if (is_array($form_state['values']['status'])) {
      foreach ($form_state['values']['status'] as $key => $choice) {
        // Always enable the default theme, despite its status checkbox being checked:
        if ($choice || $form_state['values']['theme_default'] == $key) {
          system_initialize_theme_blocks($key);
          $new_theme_list[] = $key;
          db_query("UPDATE {system} SET status = 1 WHERE type = 'theme' and name = '%s'", $key);
        }
      }
    }
    if (($admin_theme = variable_get('admin_theme', '0')) != '0' && $admin_theme != $form_state['values']['theme_default']) {
      drupal_set_message(t('Please note that the <a href="!admin_theme_page">administration theme</a> is still set to the %admin_theme theme; consequently, the theme on this page remains unchanged. All non-administrative sections of the site, however, will show the selected %selected_theme theme by default.', array(
        '!admin_theme_page' => url('admin/settings/admin'),
        '%admin_theme' => $admin_theme,
        '%selected_theme' => $form_state['values']['theme_default'],
      )));
    }
    variable_set('theme_default', $form_state['values']['theme_default']);
  }
  else {
    // Revert to defaults: only Garland is enabled.
    variable_del('theme_default');
    db_query("UPDATE {system} SET status = 1 WHERE type = 'theme' AND name = 'garland'");
    $new_theme_list = array('garland');
  }

  list_themes(TRUE);
  menu_rebuild();
  drupal_set_message(t('The configuration options have been saved.'));
  $form_state['redirect'] = 'admin/build/themes';

  // Notify locale module about new themes being enabled, so translations can
  // be imported. This might start a batch, and only return to the redirect
  // path after that.
  module_invoke('locale', 'system_update', array_diff($new_theme_list, $old_theme_list));

  return;
}

/**
 * Form builder; display theme configuration for entire site and individual themes.
 * @param $key
 *   A theme name.
 * @return
 *   The form structure.
 */
function system_theme_settings(&$form_state, $key = '') {
  $directory_path = file_directory_path();
  file_check_directory($directory_path, FILE_CREATE_DIRECTORY, 'file_directory_path');

  // Default settings are defined in theme_get_settings() in includes/theme.inc
  if ($key) {
    $settings = theme_get_settings($key);
    $var = str_replace('/', '_', 'theme_' . $key . '_settings');
    $themes = system_theme_data();
    $features = $themes[$key]->info['features'];
  }
  else {
    $settings = theme_get_settings('');
    $var = 'theme_settings';
  }

  $form['var'] = array('#type' => 'hidden', '#value' => $var);

  // Check for a new uploaded logo, and use that instead.
  if ($file = file_save_upload('logo_upload', array('file_validate_is_image' => array()))) {
    $parts = pathinfo($file->filename);
    $filename = ($key) ? str_replace('/', '_', $key) . '_logo.' . $parts['extension'] : 'logo.' . $parts['extension'];

    // The image was saved using file_save_upload() and was added to the
    // files table as a temporary file. We'll make a copy and let the garbage
    // collector delete the original upload.
    if ($filepath = file_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
      $_POST['default_logo'] = 0;
      $_POST['toggle_logo'] = 1;
    }
  }

  // Check for a new uploaded favicon, and use that instead.
  if ($file = file_save_upload('favicon_upload')) {
    $parts = pathinfo($file->filename);
    $filename = ($key) ? str_replace('/', '_', $key) . '_favicon.' . $parts['extension'] : 'favicon.' . $parts['extension'];

    // The image was saved using file_save_upload() and was added to the
    // files table as a temporary file. We'll make a copy and let the garbage
    // collector delete the original upload.
    if ($filepath = file_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
      $_POST['default_favicon'] = 0;
      $_POST['toggle_favicon'] = 1;
    }
  }

  // Toggle settings
  $toggles = array(
    'logo'                 => t('Logo'),
    'name'                 => t('Site name'),
    'slogan'               => t('Site slogan'),
    'mission'              => t('Mission statement'),
    'node_user_picture'    => t('User pictures in posts'),
    'comment_user_picture' => t('User pictures in comments'),
    'search'               => t('Search box'),
    'favicon'              => t('Shortcut icon'),
    'main_menu'            => t('Main menu'),
    'secondary_menu'      => t('Secondary menu'),
  );

  // Some features are not always available
  $disabled = array();
  if (!variable_get('user_pictures', 0)) {
    $disabled['toggle_node_user_picture'] = TRUE;
    $disabled['toggle_comment_user_picture'] = TRUE;
  }
  if (!module_exists('search')) {
    $disabled['toggle_search'] = TRUE;
  }

  $form['theme_settings'] = array(
    '#type' => 'fieldset',
    '#title' => t('Toggle display'),
    '#description' => t('Enable or disable the display of certain page elements.'),
  );
  foreach ($toggles as $name => $title) {
    if ((!$key) || in_array($name, $features)) {
      $form['theme_settings']['toggle_' . $name] = array('#type' => 'checkbox', '#title' => $title, '#default_value' => $settings['toggle_' . $name]);
      // Disable checkboxes for features not supported in the current configuration.
      if (isset($disabled['toggle_' . $name])) {
        $form['theme_settings']['toggle_' . $name]['#disabled'] = TRUE;
      }
    }
  }

  // System wide only settings.
  if (!$key) {
    // Create neat 2-column layout for the toggles
    $form['theme_settings'] += array(
      '#prefix' => '<div class="theme-settings-left">',
      '#suffix' => '</div>',
    );

    // Toggle node display.
    $node_types = node_get_types('names');
    if ($node_types) {
      $form['node_info'] = array(
        '#type' => 'fieldset',
        '#title' => t('Display post information on'),
        '#description' => t('Enable or disable the <em>submitted by Username on date</em> text when displaying posts of the following type.'),
        '#prefix' => '<div class="theme-settings-right">',
        '#suffix' => '</div>',
      );
      foreach ($node_types as $type => $name) {
        $form['node_info']["toggle_node_info_$type"] = array('#type' => 'checkbox', '#title' => check_plain($name), '#default_value' => $settings["toggle_node_info_$type"]);
  elseif (!element_children($form['theme_settings'])) {
    // If there is no element in the theme settings fieldset then do not show
    // it -- but keep it in the form if another module wants to alter.
    $form['theme_settings']['#access'] = FALSE;
  }

  // Logo settings
  if ((!$key) || in_array('logo', $features)) {
    $form['logo'] = array(
      '#type' => 'fieldset',
      '#title' => t('Logo image settings'),
      '#description' => t('If toggled on, the following logo will be displayed.'),
      '#attributes' => array('class' => 'theme-settings-bottom'),
    );
    $form['logo']["default_logo"] = array(
      '#type' => 'checkbox',
      '#title' => t('Use the default logo'),
      '#default_value' => $settings['default_logo'],
      '#tree' => FALSE,
      '#description' => t('Check here if you want the theme to use the logo supplied with it.')
    );
    $form['logo']['logo_path'] = array(
      '#type' => 'textfield',
      '#title' => t('Path to custom logo'),
      '#default_value' => $settings['logo_path'],
      '#description' => t('The path to the file you would like to use as your logo file instead of the default logo.'));

    $form['logo']['logo_upload'] = array(
      '#type' => 'file',
      '#title' => t('Upload logo image'),
      '#maxlength' => 40,
      '#description' => t("If you don't have direct file access to the server, use this field to upload your logo.")
    );
  }

  if ((!$key) || in_array('favicon', $features)) {
    $form['favicon'] = array(
      '#type' => 'fieldset',
      '#title' => t('Shortcut icon settings'),
      '#description' => t("Your shortcut icon, or 'favicon', is displayed in the address bar and bookmarks of most browsers.")
    );
    $form['favicon']['default_favicon'] = array(
      '#type' => 'checkbox',
      '#title' => t('Use the default shortcut icon.'),
      '#default_value' => $settings['default_favicon'],
      '#description' => t('Check here if you want the theme to use the default shortcut icon.')
    );
    $form['favicon']['favicon_path'] = array(
      '#type' => 'textfield',
      '#title' => t('Path to custom icon'),
      '#default_value' => $settings['favicon_path'],
      '#description' => t('The path to the image file you would like to use as your custom shortcut icon.')
    );

    $form['favicon']['favicon_upload'] = array(
      '#type' => 'file',
      '#title' => t('Upload icon image'),
      '#description' => t("If you don't have direct file access to the server, use this field to upload your shortcut icon.")
    );
  }

  if ($key) {
    // Include the theme's theme-settings.php file
    $filename = './' . str_replace("/$key.info", '', $themes[$key]->filename) . '/theme-settings.php';
    if (!file_exists($filename) and !empty($themes[$key]->info['base theme'])) {
      // If the theme doesn't have a theme-settings.php file, use the base theme's.
      $base = $themes[$key]->info['base theme'];
      $filename = './' . str_replace("/$base.info", '', $themes[$base]->filename) . '/theme-settings.php';
    }
    if (file_exists($filename)) {
      require_once $filename;
    }

    // Call engine-specific settings.
    $function = $themes[$key]->prefix . '_engine_settings';
    if (function_exists($function)) {
      $group = $function($settings);
      if (!empty($group)) {
        $form['engine_specific'] = array('#type' => 'fieldset', '#title' => t('Theme-engine-specific settings'), '#description' => t('These settings only exist for all the templates and styles based on the %engine theme engine.', array('%engine' => $themes[$key]->prefix)));
        $form['engine_specific'] = array_merge($form['engine_specific'], $group);
      $function = $themes[$key]->prefix . '_settings';
    }
    if (function_exists($function)) {
      $group = $function($settings);
      if (!empty($group)) {
        $form['theme_specific'] = array('#type' => 'fieldset', '#title' => t('Theme-specific settings'), '#description' => t('These settings only exist for the %theme theme and all the styles based on it.', array('%theme' => $themes[$key]->info['name'])));
        $form['theme_specific'] = array_merge($form['theme_specific'], $group);
      }
    }
  }
  $form['#attributes'] = array('enctype' => 'multipart/form-data');

  $form = system_settings_form($form);
  // We don't want to call system_settings_form_submit(), so change #submit.
  $form['#submit'] = array('system_theme_settings_submit');
  return $form;
}

/**
 * Process system_theme_settings form submissions.
 */
function system_theme_settings_submit($form, &$form_state) {
  $values = $form_state['values'];
  $key = $values['var'];
  if ($values['op'] == t('Reset to defaults')) {
    variable_del($key);
    drupal_set_message(t('The configuration options have been reset to their default values.'));
  }
  else {
    // Exclude unnecessary elements before saving.
    unset($values['var'], $values['submit'], $values['reset'], $values['form_id'], $values['op'], $values['form_build_id'], $values['form_token']);
    variable_set($key, $values);
    drupal_set_message(t('The configuration options have been saved.'));
  }

  cache_clear_all();
}

 * Recursively check compatibility.
 *
 * @param $incompatible
 *   An associative array which at the end of the check contains all incompatible files as the keys, their values being TRUE.
 * @param $files
 *   The set of files that will be tested.
 * @param $file
 *   The file at which the check starts.
 * @return
 *   Returns TRUE if an incompatible file is found, NULL (no return value) otherwise.
 */
function _system_is_incompatible(&$incompatible, $files, $file) {
  static $seen;
  // We need to protect ourselves in case of a circular dependency.
  if (isset($seen[$file->name])) {
    return isset($incompatible[$file->name]);
  }
  $seen[$file->name] = TRUE;
  if (isset($incompatible[$file->name])) {
    return TRUE;
  }
  // The 'dependencies' key in .info files was a string in Drupal 5, but changed
  // to an array in Drupal 6. If it is not an array, the module is not
  // compatible and we can skip the check below which requires an array.
  if (!is_array($file->info['dependencies'])) {
    $file->info['dependencies'] = array();
    $incompatible[$file->name] = TRUE;
    return TRUE;
  }
  // Recursively traverse the dependencies, looking for incompatible modules
  foreach ($file->info['dependencies'] as $dependency) {
    if (isset($files[$dependency]) && _system_is_incompatible($incompatible, $files, $files[$dependency])) {
      $incompatible[$file->name] = TRUE;
      return TRUE;
    }
  }
}

/**
 * Menu callback; provides module enable/disable interface.
 *
 * The list of modules gets populated by module.info files, which contain each module's name,
 * description and dependencies.
 * @see drupal_parse_info_file for information on module.info descriptors.
 *
 * Dependency checking is performed to ensure that a module cannot be enabled if the module has
 * disabled dependencies and also to ensure that the module cannot be disabled if the module has
 * enabled dependents.
 *
 * @param $form_state
 *   An associative array containing the current state of the form.
 * @see theme_system_modules()
 * @see system_modules_submit()
 * @return
 *   The form array.
 */
function system_modules($form_state = array()) {
  node_types_rebuild();
  menu_rebuild();
  cache_clear_all('schema', 'cache');
  // Get current list of modules.
  $files = module_rebuild_cache();
  // Remove hidden modules from display list.
  foreach ($files as $filename => $file) {
    if (!empty($file->info['hidden'])) {
      unset($files[$filename]);
    }
  }

  uasort($files, 'system_sort_modules_by_info_name');

  if (!empty($form_state['storage'])) {
    return system_modules_confirm_form($files, $form_state['storage']);
  }
  $dependencies = array();
  $modules = array();
  $form['modules'] = array('#tree' => TRUE);
  // Used when checking if module implements a help page.
  $help_arg = module_exists('help') ? drupal_help_arg() : FALSE;

  // The list of required modules.
  $modules_required = drupal_required_modules();
  // Iterate through each of the modules.
  foreach ($files as $filename => $module) {
    $extra = array();
    // If the module is requried, set it to be so.
    if (in_array($filename, $modules_required)) {
      $extra['required'] = TRUE;
    $extra['enabled'] = (bool) $module->status;
    // If this module has dependencies, add them to the array.
    if (is_array($module->info['dependencies'])) {
      foreach ($module->info['dependencies'] as $dependency) {
        if (!isset($files[$dependency])) {
          $extra['dependencies'][$dependency] = drupal_ucfirst($dependency) . t(' (<span class="admin-missing">missing</span>)');
          $extra['disabled'] = TRUE;
        }
        elseif (!$files[$dependency]->status) {
          $extra['dependencies'][$dependency] = $files[$dependency]->info['name'] . t(' (<span class="admin-disabled">disabled</span>)');
          $extra['dependencies'][$dependency] = $files[$dependency]->info['name'] . t(' (<span class="admin-enabled">enabled</span>)');
    }
    // Generate link for module's help page, if there is one.
    if ($help_arg && module_hook($filename, 'help')) {
      if (module_invoke($filename, 'help', "admin/help#$filename", $help_arg)) {
        // Module has a help page.
        $extra['help'] = theme('more_help_link', url("admin/help/$filename"));
      }
    }
    // Mark dependents disabled so user can not remove modules being depended on.
    $dependents = array();
    foreach ($module->info['dependents'] as $dependent) {
      if ($files[$dependent]->status == 1) {
        $extra['dependents'][] = $files[$dependent]->info['name'] . t(' (<span class="admin-enabled">enabled</span>)');
        $extra['disabled'] = TRUE;
        $extra['dependents'][] = $files[$dependent]->info['name'] . t(' (<span class="admin-disabled">disabled</span>)');
    if (!isset($module->info['package'])) {
      $module->info['package'] = 'Other';
    }
    $form['modules'][$module->info['package']][$filename] = _system_modules_build_row($module->info, $extra);
  // Add basic information to the fieldsets.
  foreach (element_children($form['modules']) as $package) {
    $form['modules'][$package] += array(
      '#type' => 'fieldset',
      '#title' => t($package),
      '#collapsible' => TRUE,
      '#collapsed' => ($package == 'Core - required'),
      '#theme' => 'system_modules_fieldset',
      '#header' => array(
        array('data' => t('Enabled'), 'class' => 'checkbox'),
        t('Name'),
        t('Version'),
        t('Description'),
      ),
    );
    '#type' => 'submit',
    '#value' => t('Save configuration'),
  );
  $form['#action'] = url('admin/build/modules/list/confirm');

  return $form;
}

 * Array sorting callback; sorts modules or themes by their name.
function system_sort_modules_by_info_name($a, $b) {
  return strcasecmp($a->info['name'], $b->info['name']);
}

 * Build a table row for the system modules page.
function _system_modules_build_row($info, $extra) {
  // Add in the defaults.
  $extra += array(
    'required' => FALSE,
    'dependencies' => array(),
    'dependents' => array(),
    'disabled' => FALSE,
    'enabled' => FALSE,
    'help' => '',
  );
  $form = array(
    '#tree' => TRUE,
  );
  // Set the basic properties.
  $form['name'] = array(
    '#markup' => t($info['name']),
  );
  $form['description'] = array(
    '#markup' => t($info['description']),
  );
  $form['version'] = array(
    '#markup' => $info['version'],
  );
  $form['#dependencies'] = $extra['dependencies'];
  $form['#dependents'] = $extra['dependents'];

  // Check the compatibilities.
  $compatible = TRUE;
  $status_short = '';
  $status_long = '';

  // Check the core compatibility.
  if (!isset($info['core']) || $info['core'] != DRUPAL_CORE_COMPATIBILITY) {
    $compatible = FALSE;
    $status_short .= t('Incompatible with this version of Drupal core. ');
    $status_long .= t('This version is incompatible with the !core_version version of Drupal core. ', array('!core_version' => VERSION));
  }

  // Ensure this module is compatible with the currently installed version of PHP.
  if (version_compare(phpversion(), $info['php']) < 0) {
    $compatible = FALSE;
    $status_short .= t('Incompatible with this version of PHP');
    if (substr_count($info['php'], '.') < 2) {
      $php_required .= '.*';
    }
    $status_long .= t('This module requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $php_required, '!php_version' => phpversion()));
  }

  // If this module is compatible, present a checkbox indicating
  // this module may be installed. Otherwise, show a big red X.
  if ($compatible) {
    $form['enable'] = array(
      '#type' => 'checkbox',
      '#title' => t('Enable'),
      '#required' => $extra['required'],
      '#default_value' => $extra['enabled'],
    );
    if ($extra['disabled']) {
      $form['enable']['#disabled'] = TRUE;
    }
  }
  else {
    $form['enable'] = array(
      '#markup' =>  theme('image', 'misc/watchdog-error.png', t('incompatible'), $status_short),
    );
    $form['description']['#value'] .= theme('system_modules_incompatible', $status_long);
  }

  // Show a "more help" link for modules that have them.
  if ($extra['help']) {
    $form['help'] = array(
      '#markup' => $extra['help'],
    );
  }
  return $form;
}

/**
 * Display confirmation form for dependencies.
 *
 * @param $modules
 *   Array of module file objects as returned from module_rebuild_cache().
 * @param $storage
 *   The contents of $form_state['storage']; an array with two
 *   elements: the list of dependencies and the list of status
 *   form field values from the previous screen.
 * @ingroup forms
 */
function system_modules_confirm_form($modules, $storage) {
  $form = array();
  $items = array();

  $form['validation_modules'] = array('#type' => 'value', '#value' => $modules);
  $form['status']['#tree'] = TRUE;

  foreach ($storage['dependencies'] as $info) {
      '@module' => $info['name'],
      '@dependencies' => implode(', ', $info['dependencies']),
    $items[] = format_plural(count($info['dependencies']), 'You must enable the @dependencies module to install @module.', 'You must enable the @dependencies modules to install @module.', $t_argument);
  $form['text'] = array('#markup' => theme('item_list', $items));

  if ($form) {
    // Set some default form values
    $form = confirm_form(
      $form,
      t('Some required modules must be enabled'),
      'admin/build/modules',
      t('Would you like to continue with enabling the above?'),
      t('Continue'),
      t('Cancel'));
    return $form;
  }
}

/**
 * Submit callback; handles modules form submission.
 */
function system_modules_submit($form, &$form_state) {
  include_once './includes/install.inc';
  $modules = array();
  // If we're not coming from the confirmation form, build the list of modules.
  if (!isset($form_state['storage'])) {
    foreach ($form_state['values']['modules'] as $group_name => $group) {
      foreach ($group as $module => $enabled) {
        $modules[$module] = array('group' => $group_name, 'enabled' => $enabled['enable']);
    // If we are coming from the confirmation form, fetch
    // the modules out of $form_state.
    $modules = $form_state['storage']['modules'];
  // Get a list of all modules, for building dependencies with.
  $files = module_rebuild_cache();

  // The modules to be enabled.
  $enabled_modules = array();
  // The modules to be disabled.
  $disable_modules = array();
  // The modules to be installed.
  $new_modules = array();
  // The un-met dependencies.
  $dependencies = array();
  // Go through each module, finding out
  // if we should enable, install, or disable it,
  // and if it has any un-met dependencies.
  foreach ($modules as $name => $module) {
    // If it's enabled, find out whether to just
    // enable it, or install it.
    if ($module['enabled']) {
      if (drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) {
        $new_modules[$name] = $name;
      }
      else {
        $enable_modules[$name] = $name;
      }
      // If we're not coming from a confirmation form,
      // search dependencies. Otherwise, the user will have already
      // approved of the depdent modules being enabled.
      if (empty($form_state['storage'])) {
        foreach ($form['modules'][$module['group']][$name]['#dependencies'] as $dependency => $string) {
          if (!isset($dependencies[$name])) {
            $dependencies[$name] = array(
              'name' => $files[$name]->info['name'],
              'dependencies' => array($dependency => $files[$dependency]->info['name']),
            );
          }
          else {
            $dependencies[$name]['dependencies'][$dependency] = $files[$dependency]->info['name'];
          }
          $modules[$dependency] = array('group' => $files[$dependency]->info['package'], 'enabled' => TRUE);
        }
      }
    }
    else {
      $disable_modules[$name] = $name;
    }
  }
    // If there where un-met dependencies and they haven't confirmed don't process
    // the submission yet. Store the form submission data needed later.
    if (!isset($form_state['values']['confirm'])) {
      $form_state['storage'] = array('dependencies' => $dependencies, 'modules' => $modules);
    // Otherwise, install or enable the modules.
      $dependencies = $form_storage['dependencies'];
      foreach ($dependencies as $info) {
        foreach ($info['dependencies'] as $dependency => $name) {
          if (drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) {
            $new_modules[$name] = $name;
          }
          else {
            $enable_modules[$name] = $name;
          }
        }
      }
    }
  }
  // If we have no dependencies, or the dependencies are confirmed
  // to be installed, we don't need the temporary storage anymore.
  unset($form_state['storage']);

  $old_module_list = module_list();

  // Enable the modules needing enabling.
  if (!empty($enable_modules)) {
    module_enable($enable_modules);
  }
  // Disable the modules that need disabling.
  if (!empty($disable_modules)) {
    module_disable($disable_modules);
  }

  // Install new modules.
  if (!empty($new_modules)) {
    foreach ($new_modules as $key => $module) {
      if (!drupal_check_module($module)) {
        unset($new_modules[$key]);
      }
    drupal_install_modules($new_modules);
  }

  $current_module_list = module_list(TRUE, FALSE);
  if ($old_module_list != $current_module_list) {
    drupal_set_message(t('The configuration options have been saved.'));
  }

  drupal_clear_css_cache();
  drupal_clear_js_cache();

  $form_state['redirect'] = 'admin/build/modules';

  // Notify locale module about module changes, so translations can be
  // imported. This might start a batch, and only return to the redirect
  // path after that.
  module_invoke('locale', 'system_update', $new_modules);

  // Synchronize to catch any actions that were added or removed.
  actions_synchronize();

  return;
}

/**
 * Uninstall functions
 */

/**
 * Builds a form of currently disabled modules.
 *
 * @ingroup forms
 * @see system_modules_uninstall_validate()
 * @see system_modules_uninstall_submit()
 * @param $form_state['values']
 *   Submitted form values.
 * @return
 *   A form array representing the currently disabled modules.
 */
function system_modules_uninstall($form_state = NULL) {
  // Make sure the install API is available.
  include_once './includes/install.inc';

  // Display the confirm form if any modules have been submitted.
  if (isset($form_state) && $confirm_form = system_modules_uninstall_confirm_form($form_state['storage'])) {
    return $confirm_form;
  }

  $form = array();

  // Pull all disabled modules from the system table.
  $disabled_modules = db_query("SELECT name, filename, info FROM {system} WHERE type = 'module' AND status = 0 AND schema_version > %d ORDER BY name", SCHEMA_UNINSTALLED);
  while ($module = db_fetch_object($disabled_modules)) {