system.admin.inc 83.1 KB
Newer Older
1 2 3
<?php

/**
4 5 6 7 8 9
 * @file
 * Admin page callbacks for the system module.
 */

/**
 * Menu callback; Provide the administration overview page.
10 11 12 13 14 15 16 17 18 19
 */
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.
20
  if (system_status(TRUE) && user_access('administer site configuration')) {
21
    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');
22
  }
23
  $blocks = array();
24 25 26 27 28 29 30 31 32 33 34 35 36
  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.
37 38
      if (!empty($item['localized_options']['attributes']['title'])) {
        $item['description'] = $item['localized_options']['attributes']['title'];
39 40 41 42 43 44 45 46 47 48 49
      }
      $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;
50 51
    }
  }
52 53 54 55 56 57 58
  if ($blocks) {
    ksort($blocks);
    return theme('admin_page', $blocks);
  }
  else {
    return t('You do not have any administrative items.');
  }
59 60 61 62 63
}


/**
 * Provide a single block from the administration menu as a page.
64
 *
65 66 67 68
 * This function is often a destination for these blocks.
 * For example, 'admin/content/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.
69 70 71
 *
 * @return
 *   The output HTML.
72 73 74
 */
function system_admin_menu_block_page() {
  $item = menu_get_item();
75 76 77 78 79 80
  if ($content = system_admin_menu_block($item)) {
    $output = theme('admin_block_content', $content);
  }
  else {
    $output = t('You do not have any administrative items.');
  }
81 82
  return $output;
}
83 84 85

/**
 * Menu callback; Sets whether the admin menu is in compact mode or not.
86 87 88
 *
 * @param $mode
 *   Valid values are 'on' and 'off'.
89 90 91 92 93 94 95 96 97 98 99 100 101 102
 */
function system_admin_compact_page($mode = 'off') {
  global $user;
  user_save($user, array('admin_compact_mode' => ($mode == 'on')));
  drupal_goto('admin');
}

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

  $modules = module_rebuild_cache();
  $menu_items = array();
103
  $help_arg = module_exists('help') ? drupal_help_arg() : FALSE;
104 105 106 107 108 109 110 111 112 113 114 115 116

  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.
117
      if ($help_arg && module_invoke($module, 'help', "admin/help#$module", $help_arg)) {
118 119 120 121 122 123 124 125 126 127 128 129 130
        $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);
}

/**
131
 * Menu callback: Displays the configuration overview page.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
 */
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.
149
 *
150
 * @ingroup forms
151
 * @see system_settings_form()
152 153 154
 */
function system_admin_theme_settings() {
  $themes = system_theme_data();
155 156 157

  uasort($themes, 'system_sort_modules_by_info_name');

158
  $options[0] = '<'. t('System default') .'>';
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
  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.
184
 *
185
 * @ingroup forms
186
 * @see system_themes_form_submit()
187 188 189 190
 */
function system_themes_form() {

  $themes = system_theme_data();
191 192 193

  uasort($themes, 'system_sort_modules_by_info_name');

194 195 196 197 198 199
  $status = array();
  $incompatible_core = array();
  $incompatible_php = array();

  foreach ($themes as $theme) {
    $screenshot = NULL;
200 201 202 203 204 205 206 207 208 209 210
    // Create a list which includes the current theme and all its base themes.
    if (isset($themes[$theme->name]->base_themes)) {
      $theme_keys = array_keys($themes[$theme->name]->base_themes);
      $theme_keys[] = $theme->name;
    }
    else {
      $theme_keys = array($theme->name);
    }
    // Look for a screenshot in the current theme or in its closest ancestor.
    foreach (array_reverse($theme_keys) as $theme_key) {
      if (isset($themes[$theme_key]) && file_exists($themes[$theme_key]->info['screenshot'])) {
211 212 213 214 215 216 217 218 219 220 221 222
        $screenshot = $themes[$theme_key]->info['screenshot'];
        break;
      }
    }
    $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('#value' => $screenshot);
    $form[$theme->name]['info'] = array(
      '#type' => 'value',
      '#value' => $theme->info,
    );
    $options[$theme->name] = '';
223 224

    if (!empty($theme->status) || $theme->name == variable_get('admin_theme', '0')) {
225 226 227 228 229
      $form[$theme->name]['operations'] = array('#value' => 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();
230 231 232 233 234
    }
    if (!empty($theme->status)) {
      $status[] = $theme->name;
    }
    else {
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
      // 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;
}

268 269 270
/**
 * Process system_themes_form form submissions.
 */
271
function system_themes_form_submit($form, &$form_state) {
272
  drupal_clear_css_cache();
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311

  // 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();
312
  drupal_rebuild_theme_registry();
313 314 315 316 317 318 319 320 321 322 323 324 325
  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.
326
 *
327 328 329 330
 * @param $key
 *   A theme name.
 * @return
 *   The form structure.
331
 * @ingroup forms
332
 * @see system_theme_settings_submit()
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
 */
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 (file_copy($file, $filename, FILE_EXISTS_REPLACE)) {
      $_POST['default_logo'] = 0;
      $_POST['logo_path'] = $file->filepath;
      $_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 (file_copy($file, $filename)) {
      $_POST['default_favicon'] = 0;
      $_POST['favicon_path'] = $file->filepath;
      $_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'),
    'primary_links'        => t('Primary links'),
    'secondary_links'      => t('Secondary links'),
  );

  // 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]);
414 415
      // Disable checkboxes for features not supported in the current configuration.
      if (isset($disabled['toggle_'. $name])) {
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
        $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'),
435
        '#description' => t('Enable or disable the <em>submitted by Username on date</em> text when displaying posts of the following type.'),
436 437 438 439
        '#prefix' => '<div class="theme-settings-right">',
        '#suffix' => '</div>',
      );
      foreach ($node_types as $type => $name) {
440
        $form['node_info']["toggle_node_info_$type"] = array('#type' => 'checkbox', '#title' => check_plain($name), '#default_value' => $settings["toggle_node_info_$type"]);
441 442 443
      }
    }
  }
444 445 446 447 448
  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;
  }
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482

  // 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'),
483
      '#description' => t("Your shortcut icon, or 'favicon', is displayed in the address bar and bookmarks of most browsers.")
484 485 486 487 488 489 490 491 492 493
    );
    $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'),
494
      '#default_value' => $settings['favicon_path'],
495 496 497 498 499 500 501 502 503 504 505
      '#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) {
506 507
    // Call engine-specific settings.
    $function = $themes[$key]->prefix .'_engine_settings';
508
    if (function_exists($function)) {
509 510 511 512
      $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);
513
      }
514
    }
515 516 517 518 519

    // Create a list which includes the current theme and all its base themes.
    if (isset($themes[$key]->base_themes)) {
      $theme_keys = array_keys($themes[$key]->base_themes);
      $theme_keys[] = $key;
520
    }
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
    else {
      $theme_keys = array($key);
    }

    // Process the theme and all its base themes.
    foreach ($theme_keys as $theme) {
      // Include the theme-settings.php file.
      $filename = './'. str_replace("/$theme.info", '', $themes[$theme]->filename) .'/theme-settings.php';
      if (file_exists($filename)) {
        require_once $filename;
      }

      $function = $theme .'_settings';
      if (!function_exists($function)) {
        $function = $themes[$theme]->prefix .'_settings';
      }
      if (function_exists($function)) {
        $group = $function($settings);
        if (!empty($group)) {
          $form['theme_specific']['#type'] = 'fieldset';
          $form['theme_specific']['#title'] = t('Theme-specific settings');
          $form['theme_specific']['#description'] = t('These settings only exist for the %theme theme and all the styles based on it.', array('%theme' => $themes[$theme]->info['name']));
          $form['theme_specific'] = array_merge($form['theme_specific'], $group);
        }
545 546 547 548 549 550 551 552 553 554 555
      }
    }
  }
  $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;
}

556 557 558
/**
 * Process system_theme_settings form submissions.
 */
559
function system_theme_settings_submit($form, &$form_state) {
560 561
  $values = $form_state['values'];
  $key = $values['var'];
562

563
  if ($values['op'] == t('Reset to defaults')) {
564 565 566 567
    variable_del($key);
    drupal_set_message(t('The configuration options have been reset to their default values.'));
  }
  else {
568 569 570
    // 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);
571 572 573 574 575 576
    drupal_set_message(t('The configuration options have been saved.'));
  }

  cache_clear_all();
}

577
/**
578 579 580 581 582 583 584 585 586 587
 * 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.
588 589
 */
function _system_is_incompatible(&$incompatible, $files, $file) {
590 591 592 593 594 595
  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;
596 597 598
  if (isset($incompatible[$file->name])) {
    return TRUE;
  }
599 600
  // 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
601
  // compatible and we can skip the check below which requires an array.
602 603 604 605 606
  if (!is_array($file->info['dependencies'])) {
    $file->info['dependencies'] = array();
    $incompatible[$file->name] = TRUE;
    return TRUE;
  }
607 608 609 610 611 612 613 614 615
  // 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;
    }
  }
}

616 617 618 619 620 621 622 623 624 625 626 627
/**
 * Menu callback; provides module enable/disable interface.
 *
 * Modules can be enabled or disabled and set for throttling if the throttle module is enabled.
 * 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.
 *
628 629
 * @param $form_state
 *   An associative array containing the current state of the form.
630
 * @ingroup forms
631 632
 * @see theme_system_modules()
 * @see system_modules_submit()
633 634 635 636 637 638
 * @return
 *   The form array.
 */
function system_modules($form_state = array()) {
  // Get current list of modules.
  $files = module_rebuild_cache();
639

640 641 642 643 644 645 646 647 648
  // Remove hidden modules from display list.
  $visible_files = $files;
  foreach ($visible_files as $filename => $file) {
    if (!empty($file->info['hidden'])) {
      unset($visible_files[$filename]);
    }
  }

  uasort($visible_files, 'system_sort_modules_by_info_name');
649

650
  if (!empty($form_state['storage'])) {
651
    return system_modules_confirm_form($visible_files, $form_state['storage']);
652 653 654 655
  }
  $dependencies = array();

  // Store module list for validation callback.
656
  $form['validation_modules'] = array('#type' => 'value', '#value' => $visible_files);
657 658 659 660

  // Create storage for disabled modules as browser will disable checkboxes.
  $form['disabled_modules'] = array('#type' => 'value', '#value' => array());

661
  // Traverse the files, checking for compatibility
662 663
  $incompatible_core = array();
  $incompatible_php = array();
664
  foreach ($visible_files as $filename => $file) {
665 666
    // Ensure this module is compatible with this version of core.
    if (!isset($file->info['core']) || $file->info['core'] != DRUPAL_CORE_COMPATIBILITY) {
667
      $incompatible_core[$file->name] = $file->name;
668 669 670 671
    }
    // Ensure this module is compatible with the currently installed version of PHP.
    if (version_compare(phpversion(), $file->info['php']) < 0) {
      $incompatible_php[$file->name] = $file->info['php'];
672 673 674 675 676 677 678
    }
  }

  // Array for disabling checkboxes in callback system_module_disable.
  $disabled = array();
  $throttle = array();
  // Traverse the files retrieved and build the form.
679
  foreach ($visible_files as $filename => $file) {
680 681 682 683 684 685
    $form['name'][$filename] = array('#value' => $file->info['name']);
    $form['version'][$filename] = array('#value' => $file->info['version']);
    $form['description'][$filename] = array('#value' => t($file->info['description']));
    $options[$filename] = '';
    // Ensure this module is compatible with this version of core and php.
    if (_system_is_incompatible($incompatible_core, $files, $file) || _system_is_incompatible($incompatible_php, $files, $file)) {
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
      $disabled[] = $file->name;
      // Nothing else in this loop matters, so move to the next module.
      continue;
    }
    if ($file->status) {
      $status[] = $file->name;
    }
    if ($file->throttle) {
      $throttle[] = $file->name;
    }

    $dependencies = array();
    // Check for missing dependencies.
    if (is_array($file->info['dependencies'])) {
      foreach ($file->info['dependencies'] as $dependency) {
701 702 703 704 705 706 707 708 709
        if (!isset($files[$dependency])) {
          $dependencies[] = t('@module (<span class="admin-missing">missing</span>)', array('@module' => drupal_ucfirst($dependency)));
          $disabled[] = $filename;
          $form['disabled_modules']['#value'][$filename] = FALSE;
        }
        // Only display visible modules.
        elseif (isset($visible_files[$dependency])) {
          if ($files[$dependency]->status) {
            $dependencies[] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $files[$dependency]->info['name']));
710 711
          }
          else {
712
            $dependencies[] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $files[$dependency]->info['name']));
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
          }
        }
      }

      // Add text for dependencies.
      if (!empty($dependencies)) {
        $form['description'][$filename]['dependencies'] = array(
          '#value' => t('Depends on: !dependencies', array('!dependencies' => implode(', ', $dependencies))),
          '#prefix' => '<div class="admin-dependencies">',
          '#suffix' => '</div>',
        );
      }
    }

    // Mark dependents disabled so user can not remove modules being depended on.
    $dependents = array();
    foreach ($file->info['dependents'] as $dependent) {
730 731 732 733 734 735 736 737 738 739
      // Hidden modules are unset already.
      if (isset($visible_files[$dependent])) {
        if ($files[$dependent]->status == 1) {
          $dependents[] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $files[$dependent]->info['name']));
          $disabled[] = $filename;
          $form['disabled_modules']['#value'][$filename] = TRUE;
        }
        else {
          $dependents[] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $files[$dependent]->info['name']));
        }
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
      }
    }

    // Add text for enabled dependents.
    if (!empty($dependents)) {
      $form['description'][$filename]['required'] = array(
        '#value' => t('Required by: !required', array('!required' => implode(', ', $dependents))),
        '#prefix' => '<div class="admin-required">',
        '#suffix' => '</div>',
      );
    }
  }

  $modules_required = drupal_required_modules();
  // Merge in required modules.
  foreach ($modules_required as $required) {
    $disabled[] = $required;
    $form['disabled_modules']['#value'][$required] = TRUE;
  }

  // Handle status checkboxes, including overriding
  // the generated checkboxes for required modules.
  $form['status'] = array(
    '#type' => 'checkboxes',
    '#default_value' => $status,
    '#options' => $options,
    '#process' => array(
      'expand_checkboxes',
      'system_modules_disable',
    ),
    '#disabled_modules' => $disabled,
771
    '#incompatible_modules_core' => $incompatible_core,
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
    '#incompatible_modules_php' => $incompatible_php,
  );

  // Handle throttle checkboxes, including overriding the
  // generated checkboxes for required modules.
  if (module_exists('throttle')) {
    $form['throttle'] = array(
      '#type' => 'checkboxes',
      '#default_value' => $throttle,
      '#options' => $options,
      '#process' => array(
        'expand_checkboxes',
        'system_modules_disable',
      ),
      '#disabled_modules' => array_merge($modules_required, array('throttle')),
    );
  }

  $form['buttons']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save configuration'),
  );
  $form['#action'] = url('admin/build/modules/list/confirm');

  return $form;
}

799
/**
800
 * Array sorting callback; sorts modules or themes by their name.
801
 */
802 803 804 805
function system_sort_modules_by_info_name($a, $b) {
  return strcasecmp($a->info['name'], $b->info['name']);
}

806 807
/**
 * Form process callback function to disable check boxes.
808 809 810 811 812 813 814 815
 *
 * @param $form
 *   The form structure.
 * @param $edit
 *   Not used.
 * @ingroup forms
 * @return
 *   The form structure.
816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
 */
function system_modules_disable($form, $edit) {
  foreach ($form['#disabled_modules'] as $key) {
    $form[$key]['#attributes']['disabled'] = 'disabled';
  }
  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();

  list($dependencies, $status) = $storage;
  $form['validation_modules'] = array('#type' => 'value', '#value' => $modules);
  $form['status']['#tree'] = TRUE;
  // Remember list of modules selected on the module listing page already.
  foreach ($status as $key => $choice) {
    $form['status'][$key] = array('#type' => 'value', '#value' => $choice);
  }
  foreach ($dependencies as $name => $missing_dependencies) {
    $form['status'][$name] = array('#type' => 'hidden', '#value' => 1);
    foreach ($missing_dependencies as $k => $dependency) {
      $form['status'][$dependency] = array('#type' => 'hidden', '#value' => 1);
      $info = $modules[$dependency]->info;
      $missing_dependencies[$k] = $info['name'] ? $info['name'] : drupal_ucfirst($dependency);
    }
    $t_argument = array(
      '@module' => $modules[$name]->info['name'],
      '@dependencies' => implode(', ', $missing_dependencies),
    );
    $items[] = format_plural(count($missing_dependencies), 'You must enable the @dependencies module to install @module.', 'You must enable the @dependencies modules to install @module.', $t_argument);
  }
  $form['text'] = array('#value' => 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';
  $new_modules = array();

  // If we are coming from the confirm form...
  if (!isset($form_state['storage'])) {
    // Merge in disabled active modules since they should be enabled.
    // They don't appear because disabled checkboxes are not submitted
    // by browsers.
    $form_state['values']['status'] = array_merge($form_state['values']['status'], $form_state['values']['disabled_modules']);

    // Check values for dependency that we can't install.
    if ($dependencies = system_module_build_dependencies($form_state['values']['validation_modules'], $form_state['values'])) {
      // These are the modules that depend on existing modules.
      foreach (array_keys($dependencies) as $name) {
        $form_state['values']['status'][$name] = 0;
      }
    }
  }
  else {
    $dependencies = NULL;
  }

  // Update throttle settings, if present
  if (isset($form_state['values']['throttle'])) {
    foreach ($form_state['values']['throttle'] as $key => $choice) {
      db_query("UPDATE {system} SET throttle = %d WHERE type = 'module' and name = '%s'", $choice ? 1 : 0, $key);
    }
  }

  // If there where unmet dependencies and they haven't confirmed don't process
  // the submission yet. Store the form submission data needed later.
  if ($dependencies) {
    if (!isset($form_state['values']['confirm'])) {
      $form_state['storage'] = array($dependencies, $form_state['values']['status']);
      return;
    }
    else {
      $form_state['values']['status'] = array_merge($form_state['values']['status'], $form_storage[1]);
    }
  }
  // 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']);

  $enable_modules = array();
  $disable_modules = array();
  foreach ($form_state['values']['status'] as $key => $choice) {
    if ($choice) {
      if (drupal_get_installed_schema_version($key) == SCHEMA_UNINSTALLED) {
        $new_modules[] = $key;
      }
      else {
        $enable_modules[] = $key;
      }
    }
    else {
      $disable_modules[] = $key;
    }
  }

  $old_module_list = module_list();

  if (!empty($enable_modules)) {
    module_enable($enable_modules);
  }
  if (!empty($disable_modules)) {
    module_disable($disable_modules);
  }

  // Install 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.'));
  }

960 961 962 963
  drupal_rebuild_theme_registry();
  node_types_rebuild();
  menu_rebuild();
  cache_clear_all('schema', 'cache');
964 965 966 967 968 969 970 971 972 973
  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);

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

977 978 979
  return;
}

980 981 982 983 984 985 986 987 988 989 990

/**
 * Generate a list of dependencies for modules that are going to be switched on.
 *
 * @param $modules
 *   The list of modules to check.
 * @param $form_values
 *   Submitted form values used to determine what modules have been enabled.
 * @return
 *   An array of dependencies.
 */
991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020
function system_module_build_dependencies($modules, $form_values) {
  static $dependencies;

  if (!isset($dependencies) && isset($form_values)) {
    $dependencies = array();
    foreach ($modules as $name => $module) {
      // If the module is disabled, will be switched on and it has dependencies.
      if (!$module->status && $form_values['status'][$name] && isset($module->info['dependencies'])) {
        foreach ($module->info['dependencies'] as $dependency) {
          if (!$form_values['status'][$dependency] && isset($modules[$dependency])) {
            if (!isset($dependencies[$name])) {
              $dependencies[$name] = array();
            }
            $dependencies[$name][] = $dependency;
          }
        }
      }
    }
  }
  return $dependencies;
}

/**
 * Uninstall functions
 */

/**
 * Builds a form of currently disabled modules.
 *
 * @ingroup forms
1021 1022
 * @see system_modules_uninstall_validate()
 * @see system_modules_uninstall_submit()
1023 1024
 * @param $form_state['values']
 *   Submitted form values.
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
 * @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)) {

    // Grab the module info
    $info = unserialize($module->info);

    // Load the .install file, and check for an uninstall hook.
    // If the hook exists, the module can be uninstalled.
    module_load_install($module->name);
    if (module_hook($module->name, 'uninstall')) {
      $form['modules'][$module->name]['name'] = array('#value' => $info['name'] ? $info['name'] : $module->name);
      $form['modules'][$module->name]['description'] = array('#value' => t($info['description']));
      $options[$module->name] = '';
    }
  }

  // Only build the rest of the form if there are any modules available to uninstall.
  if (!empty($options)) {
    $form['uninstall'] = array(
      '#type' => 'checkboxes',
      '#options' => $options,
    );
    $form['buttons']['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Uninstall'),
    );
    $form['#action'] = url('admin/build/modules/uninstall/confirm');
  }
  else {
    $form['modules'] = array();
  }

  return $form;
}

/**
 * Confirm uninstall of selected modules.
 *
 * @ingroup forms
1079 1080
 * @param $storage
 *   An associative array of modules selected to be uninstalled.
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
 * @return
 *   A form array representing modules to confirm.
 */
function system_modules_uninstall_confirm_form($storage) {
  // Nothing to build.
  if (!isset($storage)) {
    return;
  }

  // Construct the hidden form elements and list items.
  foreach (array_filter($storage['uninstall']) as $module => $value) {
    $info = drupal_parse_info_file(dirname(drupal_get_filename('module', $module)) .'/'. $module .'.info');
    $uninstall[] = $info['name'];
    $form['uninstall'][$module] = array('#type' => 'hidden',
      '#value' => 1,
    );
  }

  // Display a confirm form if modules have been selected.
  if (isset($uninstall)) {
    $form['#confirmed'] = TRUE;
    $form['uninstall']['#tree'] = TRUE;
    $form['modules'] = array('#value' => '<p>'. t('The following modules will be completely uninstalled from your site, and <em>all data from these modules will be lost</em>!') .'</p>'. theme('item_list', $uninstall));
    $form = confirm_form(
      $form,
      t('Confirm uninstall'),
      'admin/build/modules/uninstall',
      t('Would you like to continue with uninstalling the above?'),
      t('Uninstall'),
      t('Cancel'));
    return $form;
  }
}

/**
 * Validates the submitted uninstall form.
 */
function system_modules_uninstall_validate($form, &$form_state) {
  // Form submitted, but no modules selected.
  if (!count(array_filter($form_state['values']['uninstall']))) {
    drupal_set_message(t('No modules selected.'), 'error');
    drupal_goto('admin/build/modules/uninstall');
  }
}

/**
 * Processes the submitted uninstall form.
 */
function system_modules_uninstall_submit($form, &$form_state) {
  // Make sure the install API is available.
  include_once './includes/install.inc';

  if (!empty($form['#confirmed'])) {
    // Call the uninstall routine for each selected module.
    foreach (array_filter($form_state['values']['uninstall']) as $module => $value) {
      drupal_uninstall_module($module);
    }
    drupal_set_message(t('The selected modules have been uninstalled.'));

    unset($form_state['storage']);
    $form_state['redirect'] = 'admin/build/modules/uninstall';
  }
  else {
    $form_state['storage'] = $form_state['values'];
  }
}

/**
 * Form builder; The general site information form.
1150
 *
1151
 * @ingroup forms
1152
 * @see system_settings_form()
1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
 */
function system_site_information_settings() {
  $form['site_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Name'),
    '#default_value' => variable_get('site_name', 'Drupal'),
    '#description' => t('The name of this website.'),
    '#required' => TRUE
  );
  $form['site_mail'] = array(
    '#type' => 'textfield',
    '#title' => t('E-mail address'),
    '#default_value' => variable_get('site_mail', ini_get('sendmail_from')),
1166
    '#description' => t("The <em>From</em> address in automated e-mails sent during registration and new password requests, and other notifications. (Use an address ending in your site's domain to help prevent this e-mail being flagged as spam.)"),
1167 1168 1169 1170 1171 1172
    '#required' => TRUE,
  );
  $form['site_slogan'] = array(
    '#type' => 'textfield',
    '#title' => t('Slogan'),
    '#default_value' => variable_get('site_slogan', ''),
1173
    '#description' => t("Your site's motto, tag line, or catchphrase (often displayed alongside the title of the site).")
1174 1175 1176 1177 1178
  );
  $form['site_mission'] = array(
    '#type' => 'textarea',
    '#title' => t('Mission'),
    '#default_value' => variable_get('site_mission', ''),
1179
    '#description' => t("Your site's mission or focus statement (often prominently displayed on the front page).")
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
  );
  $form['site_footer'] = array(
    '#type' => 'textarea',
    '#title' => t('Footer message'),
    '#default_value' => variable_get('site_footer', ''),
    '#description' => t('This text will be displayed at the bottom of each page. Useful for adding a copyright notice to your pages.')
  );
  $form['anonymous'] = array(
    '#type' => 'textfield',
    '#title' => t('Anonymous user'),
    '#default_value' => variable_get('anonymous', t('Anonymous')),
1191 1192
    '#description' => t('The name used to indicate anonymous users.'),
    '#required' => TRUE,
1193 1194 1195 1196 1197 1198 1199
  );
  $form['site_frontpage'] = array(
    '#type' => 'textfield',
    '#title' => t('Default front page'),
    '#default_value' => variable_get('site_frontpage', 'node'),
    '#size' => 40,
    '#description' => t('The home page displays content from this relative URL. If unsure, specify "node".'),
1200 1201
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
    '#required' => TRUE,
1202
  );
1203
  $form['#validate'][] = 'system_site_information_settings_validate';
1204 1205 1206 1207

  return system_settings_form($form);
}

1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
/**
 * Validate the submitted site-information form.
 */
function system_site_information_settings_validate($form, &$form_state) {
  // Validate the e-mail address.
  if ($error = user_validate_mail($form_state['values']['site_mail'])) {
    form_set_error('site_mail', $error);
  }
  // Validate front page path.
  $item = array('link_path' => $form_state['values']['site_frontpage']);
1218 1219 1220 1221 1222
  $normal_path = drupal_get_normal_path($item['link_path']);
  if ($item['link_path'] != $normal_path) {
    drupal_set_message(t('The menu system stores system paths only, but will use the URL alias for display. %link_path has been stored as %normal_path', array('%link_path' => $item['link_path'], '%normal_path' => $normal_path)));
    $item['link_path'] = $normal_path;
  }
1223 1224 1225 1226 1227
  if (!empty($item) && !menu_valid_path($item)) {
    form_set_error('site_frontpage', t("The path '@path' is either invalid or you do not have access to it.", array('@path' => $item['link_path'])));
  }
}

1228 1229
/**
 * Form builder; Configure error reporting settings.
1230
 *
1231
 * @ingroup forms
1232
 * @see system_settings_form()
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247
 */
function system_error_reporting_settings() {

  $form['site_403'] = array(
    '#type' => 'textfield',
    '#title' => t('Default 403 (access denied) page'),
    '#default_value' => variable_get('site_403', ''),
    '#size' => 40,
    '#description' => t('This page is displayed when the requested document is denied to the current user. If unsure, specify nothing.'),
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=')
  );

  $form['site_404'] = array(
    '#type' => 'textfield',
    '#title' => t('Default 404 (not found) page'),
1248
    '#default_value' => variable_get('site_404', ''),
1249 1250 1251 1252 1253 1254 1255 1256
    '#size' => 40,
    '#description' => t('This page is displayed when no other content matches the requested document. If unsure, specify nothing.'),
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=')
  );

  $form['error_level'] = array(
    '#type' => 'select', '#title' => t('Error reporting'), '#default_value' => variable_get('error_level', 1),
    '#options' => array(t('Write errors to the log'), t('Write errors to the log and to the screen')),
1257
    '#description' => t('Specify where Drupal, PHP and SQL errors are logged. While it is recommended that a site running in a production environment write errors to the log only, in a development or testing environment it may be helpful to write errors both to the log and to the screen.')
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276
  );

  return system_settings_form($form);
}

/**
 * Menu callback; Menu page for the various logging options.
 */
function system_logging_overview() {
  $item = menu_get_item('admin/settings/logging');
  $content = system_admin_menu_block($item);

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

  return $output;
}

/**
 * Form builder; Configure site performance settings.
1277
 *
1278
 * @ingroup forms
1279
 * @see system_settings_form()
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
 */
function system_performance_settings() {

  $description = '<p>'. t("The normal cache mode is suitable for most sites and does not cause any side effects. The aggressive cache mode causes Drupal to skip the loading (boot) and unloading (exit) of enabled modules when serving a cached page. This results in an additional performance boost but can cause unwanted side effects.") .'</p>';

  $problem_modules = array_unique(array_merge(module_implements('boot'), module_implements('exit')));
  sort($problem_modules);

  if (count($problem_modules) > 0) {
    $description .= '<p>'. t('<strong class="error">The following enabled modules are incompatible with aggressive mode caching and will not function properly: %modules</strong>', array('%modules' => implode(', ', $problem_modules))) .'.</p>';
  }
  else {
    $description .= '<p>'. t('<strong class="ok">Currently, all enabled modules are compatible with the aggressive caching policy.</strong> Please note, if you use aggressive caching and enable new modules, you will need to check this page again to ensure compatibility.') .'</p>';
  }
  $form['page_cache'] = array(
    '#type' => 'fieldset',
    '#title' => t('Page cache'),
1297
    '#description' => t('Enabling the page cache will offer a significant performance boost. Drupal can store and send compressed cached pages requested by <em>anonymous</em> users. By caching a web page, Drupal does not have to construct the page each time it is viewed.'),
1298 1299 1300 1301 1302 1303
  );

  $form['page_cache']['cache'] = array(
    '#type' => 'radios',
    '#title' => t('Caching mode'),
    '#default_value' => variable_get('cache', CACHE_DISABLED),
1304
    '#options' => array(CACHE_DISABLED => t('Disabled'), CACHE_NORMAL => t('Normal (recommended for production sites, no side effects)'), CACHE_AGGRESSIVE => t('Aggressive (experts only, possible side effects)')),
1305 1306 1307 1308
    '#description' => $description
  );

  $period = drupal_map_assoc(array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400), 'format_interval');
1309
  $period[0] = '<'. t('none') .'>';
1310 1311 1312 1313 1314
  $form['page_cache']['cache_lifetime'] = array(
    '#type' => 'select',
    '#title' => t('Minimum cache lifetime'),
    '#default_value' => variable_get('cache_lifetime', 0),
    '#options' => $period,
1315
    '#description' => t('On high-traffic sites, it may be necessary to enforce a minimum cache lifetime. The minimum cache lifetime is the minimum amount of time that will elapse before the cache is emptied and recreated, and is applied to both page and block caches. A larger minimum cache lifetime offers better performance, but users will not see new content for a longer period of time.')
1316
  );
1317 1318 1319 1320 1321
  $form['page_cache']['page_compression'] = array(
    '#type' => 'radios',
    '#title' => t('Page compression'),
    '#default_value' => variable_get('page_compression', TRUE),
    '#options' => array(t('Disabled'), t('Enabled')),
1322
    '#description' => t("By default, Drupal compresses the pages it caches in order to save bandwidth and improve download times. This option should be disabled when using a webserver that performs compression."),
1323
  );
1324 1325 1326 1327

  $form['block_cache'] = array(
    '#type' => 'fieldset',
    '#title' => t('Block cache'),
1328
    '#description' => t('Enabling the block cache can offer a performance increase for all users by preventing blocks from being reconstructed on each page load. If the page cache is also enabled, performance increases from enabling the block cache will mainly benefit authenticated users.'),
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342
  );

  $form['block_cache']['block_cache'] = array(
    '#type' => 'radios',
    '#title' => t('Block cache'),
    '#default_value' => variable_get('block_cache', CACHE_DISABLED),
    '#options' => array(CACHE_DISABLED => t('Disabled'), CACHE_NORMAL => t('Enabled (recommended)')),
    '#disabled' => count(module_implements('node_grants')),
    '#description' => t('Note that block caching is inactive when modules defining content access restrictions are enabled.'),
  );

  $form['bandwidth_optimizations'] = array(
    '#type' => 'fieldset',
    '#title' => t('Bandwidth optimizations'),
1343
    '#description' => t('<p>Drupal can automatically optimize external resources like CSS and JavaScript, which can reduce both the size and number of requests made to your website. CSS files can be aggregated and compressed into a single file, while JavaScript files are aggregated (but not compressed). These optional optimizations may reduce server load, bandwidth requirements, and page loading times.</p><p>These options are disabled if you have not set up your files directory, or if your download method is set to private.</p>')
1344 1345 1346 1347 1348 1349 1350
  );

  $directory = file_directory_path();
  $is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC);
  $form['bandwidth_optimizations']['preprocess_css'] = array(
    '#type' => 'radios',
    '#title' => t('Optimize CSS files'),
1351
    '#default_value' => intval(variable_get('preprocess_css', 0) && $is_writable),
1352 1353
    '#disabled' => !$is_writable,
    '#options' => array(t('Disabled'), t('Enabled')),
1354
    '#description' => t('This option can interfere with theme development and should only be enabled in a production environment.'),
1355 1356 1357 1358
  );
  $form['bandwidth_optimizations']['preprocess_js'] = array(
    '#type' => 'radios',
    '#title' => t('Optimize JavaScript files'),
1359
    '#default_value' => intval(variable_get('preprocess_js', 0) && $is_writable),
1360 1361
    '#disabled' => !$is_writable,
    '#options' => array(t('Disabled'), t('Enabled')),
1362
    '#description' => t('This option can interfere with module development and should only be enabled in a production environment.'),
1363 1364
  );

1365 1366 1367 1368 1369 1370
  $form['clear_cache'] = array(
    '#type' => 'fieldset',
    '#title' => t('Clear cached data'),
    '#description' => t('Caching data improves performance, but may cause problems while troubleshooting new modules, themes, or translations, if outdated information has been cached. To refresh all cached data on your site, click the button below. <em>Warning: high-traffic sites will experience performance slowdowns while cached data is rebuilt.</em>'),
  );

1371
  $form['clear_cache']['clear'] = array(
1372 1373 1374 1375 1376
    '#type' => 'submit',
    '#value' => t('Clear cached data'),
    '#submit' => array('system_clear_cache_submit'),
  );

Dries's avatar
Dries committed
1377 1378 1379
  $form['#submit'][] = 'drupal_clear_css_cache';
  $form['#submit'][] = 'drupal_clear_js_cache';

1380 1381 1382
  return system_settings_form($form);
}

1383
/**
1384 1385 1386
 * Submit callback; clear system caches.
 *
 * @ingroup forms
1387
 */
1388
function system_clear_cache_submit($form, &$form_state) {
1389
  drupal_flush_all_caches();
1390
  drupal_set_message(t('Caches cleared.'));
1391 1392
}

1393 1394
/**
 * Form builder; Configure the site file handling.
1395
 *
1396
 * @ingroup forms
1397
 * @see system_settings_form()
1398 1399 1400 1401 1402 1403 1404 1405
 */
function system_file_system_settings() {

  $form['file_directory_path'] = array(
    '#type' => 'textfield',
    '#title' => t('File system path'),
    '#default_value' => file_directory_path(),
    '#maxlength' => 255,
1406
    '#description' => t('A file system path where the files will be stored. This directory must exist and be writable by Drupal. If the download method is set to public, this directory must be relative to the Drupal installation directory and be accessible over the web. If the download method is set to private, this directory should not be accessible over the web. Changing this location will modify all download paths and may cause unexpected problems on an existing site.'),
1407 1408 1409 1410 1411 1412 1413 1414
    '#after_build' => array('system_check_directory'),
  );

  $form['file_directory_temp'] = array(
    '#type' => 'textfield',
    '#title' => t('Temporary directory'),
    '#default_value' => file_directory_temp(),
    '#maxlength' => 255,
1415
    '#description' => t('A file system path where uploaded files will be stored during previews.'),
1416 1417 1418 1419 1420 1421 1422 1423
    '#after_build' => array('system_check_directory'),
  );

  $form['file_downloads'] = array(
    '#type' => 'radios',
    '#title' => t('Download method'),
    '#default_value' => variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC),
    '#options' => array(FILE_DOWNLOADS_PUBLIC => t('Public - files are available using HTTP directly.'), FILE_DOWNLOADS_PRIVATE => t('Private - files are transferred by Drupal.')),
1424
    '#description' => t('Choose the <em>Public download</em> method unless you wish to enforce fine-grained access controls over file downloads. Changing the download method will modify all download paths and may cause unexpected problems on an existing site.')
1425 1426 1427 1428 1429 1430 1431
  );

  return system_settings_form($form);
}

/**
 * Form builder; Configure site image toolkit usage.
1432
 *
1433
 * @ingroup forms
1434
 * @see system_settings_form()
1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456
 */
function system_image_toolkit_settings() {
  $toolkits_available = image_get_available_toolkits();
  if (count($toolkits_available) > 1) {
    $form['image_toolkit'] = array(
      '#type' => 'radios',
      '#title' => t('Select an image processing toolkit'),
      '#default_value' => variable_get('image_toolkit', image_get_toolkit()),
      '#options' => $toolkits_available
    );
  }
  elseif (count($toolkits_available) == 1) {
    variable_set('image_toolkit', key($toolkits_available));
  }

  $form['image_toolkit_settings'] = image_toolkit_invoke('settings');

  return system_settings_form($form);
}

/**
 * Form builder; Configure how the site handles RSS feeds.
1457
 *
1458
 * @ingroup forms
1459
 * @see system_settings_form()
1460 1461 1462 1463 1464
 */
function system_rss_feeds_settings() {

  $form['feed_default_items'] = array(
    '#type' => 'select',
1465
    '#title' => t('Number of items in each feed'),
1466 1467
    '#default_value' => variable_get('feed_default_items', 10),
    '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)),
1468
    '#description' => t('Default number of items to include in each feed.')
1469 1470 1471
  );
  $form['feed_item_length'] = array(
    '#type' => 'select',
1472
    '#title' => t('Feed content'),
1473 1474
    '#default_value' => variable_get('feed_item_length', 'teaser'),
    '#options' => array('title' => t('Titles only'), 'teaser' => t('Titles plus teaser'), 'fulltext' => t('Full text')),
1475
    '#description' => t('Global setting for the default display of content items in each feed.')
1476 1477 1478 1479 1480 1481 1482
  );

  return system_settings_form($form);
}

/**
 * Form builder; Configure the site date and time settings.
1483
 *
1484
 * @ingroup forms
1485 1486
 * @see system_settings_form()
 * @see system_date_time_settings_submit()
1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503
 */
function system_date_time_settings() {
  drupal_add_js(drupal_get_path('module', 'system') .'/system.js', 'module');
  drupal_add_js(array('dateTime' => array('lookup' => url('admin/settings/date-time/lookup'))), 'setting');

  // Date settings:
  $zones = _system_zonelist();

  // Date settings: possible date formats
  $date_short = array('Y-m-d H:i', 'm/d/Y - H:i', 'd/m/Y - H:i', 'Y/m/d - H:i',
           'd.m.Y - H:i', 'm/d/Y - g:ia', 'd/m/Y - g:ia', 'Y/m/d - g:ia',
           'M j Y - H:i', 'j M Y - H:i', 'Y M j - H:i',
           'M j Y - g:ia', 'j M Y - g:ia', 'Y M j - g:ia');
  $date_medium = array('D, Y-m-d H:i', 'D, m/d/Y - H:i', 'D, d/m/Y - H:i',
          'D, Y/m/d - H:i', 'F j, Y - H:i', 'j F, Y - H:i', 'Y, F j - H:i',
          'D, m/d/Y - g:ia', 'D, d/m/Y - g:ia', 'D, Y/m/d - g:ia',
          'F j, Y - g:ia', 'j F Y - g:ia', 'Y, F j - g:ia', 'j. F Y - G:i');
1504 1505
  $date_long = array('l, F j, Y - H:i', 'l, j F, Y - H:i', 'l, Y, F j - H:i',
        'l, F j, Y - g:ia', 'l, j F Y - g:ia', 'l, Y, F j - g:ia', 'l, j. F Y - G:i');
1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555

  // Date settings: construct choices for user
  foreach ($date_short as $f) {
    $date_short_choices[$f] = format_date(time(), 'custom', $f);
  }
  foreach ($date_medium as $f) {
    $date_medium_choices[$f] = format_date(time(), 'custom', $f);
  }
  foreach ($date_long as $f) {
    $date_long_choices[$f] = format_date(time(), 'custom', $f);
  }

  $date_long_choices['custom'] = $date_medium_choices['custom'] = $date_short_choices['custom'] = t('Custom format');

  $form['locale'] = array(
    '#type' => 'fieldset',
    '#title' => t('Locale settings'),
  );

  $form['locale']['date_default_timezone'] = array(
    '#type' => 'select',
    '#title' => t('Default time zone'),
    '#default_value' => variable_get('date_default_timezone', 0),
    '#options' => $zones,
    '#description' => t('Select the default site time zone.')
  );

  $form['locale']['configurable_timezones'] = array(
    '#type' => 'radios',
    '#title' => t('User-configurable time zones'),
    '#default_value' => variable_get('configurable_timezones', 1),
    '#options' => array(t('Disabled'), t('Enabled')),
    '#description' => t('When enabled, users can set their own time zone and dates will be displayed accordingly.')
  );

  $form['locale']['date_first_day'] = array(
    '#type' => 'select',
    '#title' => t('First day of week'),
    '#default_value' => variable_get('date_first_day', 0),
    '#options' => array(0 => t('Sunday'), 1 => t('Monday'), 2 => t('Tuesday'), 3 => t('Wednesday'), 4 => t('Thursday'), 5 => t('Friday'), 6 => t('Saturday')),
    '#description' => t('The first day of the week for calendar views.')
  );

  $form['date_formats'] = array(
    '#type' => 'fieldset',
    '#title' => t('Formatting'),
  );

  $date_format_short = variable_get('date_format_short', $date_short[1]);
  $form['date_formats']['date_format_short'] = array(
1556
    '#prefix' => '<div class="date-container"><div class="select-container">',
1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578
    '#suffix' => '</div>',
    '#type' => 'select',
    '#title' => t('Short date format'),
    '#attributes' => array('class' => 'date-format'),
    '#default_value' => (isset($date_short_choices[$date_format_short]) ? $date_format_short : 'custom'),
    '#options' => $date_short_choices,
    '#description' => t('The short format of date display.'),
  );

  $default_short_custom = variable_get('date_format_short_custom', (isset($date_short_choices[$date_format_short]) ? $date_format_short : ''));
  $form['date_formats']['date_format_short_custom'] = array(
    '#prefix' => '<div class="custom-container">',
    '#suffix' => '</div></div>',
    '#type' => 'textfield',
    '#title' => t('Custom short date format'),
    '#attributes' => array('class' => 'custom-format'),
    '#default_value' => $default_short_custom,
    '#description' => t('A user-defined short date format. See the <a href="@url">PHP manual</a> for available options. This format is currently set to display as <span>%date</span>.', array('@url' => 'http://php.net/manual/function.date.php', '%date' => format_date(time(), 'custom', $default_short_custom))),
  );

  $date_format_medium = variable_get('date_format_medium', $date_medium[1]);
  $form['date_formats']['date_format_medium'] = array(
1579
    '#prefix' => '<div class="date-container"><div class="select-container">',
1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601
    '#suffix' => '</div>',
    '#type' => 'select',
    '#title' => t('Medium date format'),
    '#attributes' => array('class' => 'date-format'),
    '#default_value' => (isset($date_medium_choices[$date_format_medium]) ? $date_format_medium : 'custom'),
    '#options' => $date_medium_choices,
    '#description' => t('The medium sized date display.'),
  );

  $default_medium_custom = variable_get('date_format_medium_custom', (isset($date_medium_choices[$date_format_medium]) ? $date_format_medium : ''));
  $form['date_formats']['date_format_medium_custom'] = array(
    '#prefix' => '<div class="custom-container">',
    '#suffix' => '</div></div>',
    '#type' => 'textfield',
    '#title' => t('Custom medium date format'),
    '#attributes' => array('class' => 'custom-format'),
    '#default_value' => $default_medium_custom,
    '#description' => t('A user-defined medium date format. See the <a href="@url">PHP manual</a> for available options. This format is currently set to display as <span>%date</span>.', array('@url' => 'http://php.net/manual/function.date.php', '%date' => format_date(time(), 'custom', $default_medium_custom))),
  );

  $date_format_long = variable_get('date_format_long', $date_long[0]);
  $form['date_formats']['date_format_long'] = array(
1602
    '#prefix' => '<div class="date-container"><div class="select-container">',
1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628
    '#suffix' => '</div>',
    '#type' => 'select',
    '#title' => t('Long date format'),
    '#attributes' => array('class' => 'date-format'),
    '#default_value' => (isset($date_long_choices[$date_format_long]) ? $date_format_long : 'custom'),
    '#options' => $date_long_choices,
    '#description' => t('Longer date format used for detailed display.')
  );

  $default_long_custom = variable_get('date_format_long_custom', (isset($date_long_choices[$date_format_long]) ? $date_format_long : ''));
  $form['date_formats']['date_format_long_custom'] = array(
    '#prefix' => '<div class="custom-container">',
    '#suffix' => '</div></div>',
    '#type' => 'textfield',
    '#title' => t('Custom long date format'),
    '#attributes' => array('class' => 'custom-format'),
    '#default_value' => $default_long_custom,
    '#description' => t('A user-defined long date format. See the <a href="@url">PHP manual</a> for available options. This format is currently set to display as <span>%date</span>.', array('@url' => 'http://php.net/manual/function.date.php', '%date' => format_date(time(), 'custom', $default_long_custom))),
  );

  $form = system_settings_form($form);
  // We will call system_settings_form_submit() manually, so remove it for now.
  unset($form['#submit']);
  return $form;
}

1629 1630 1631
/**
 * Process system_date_time_settings form submissions.
 */
1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649
function system_date_time_settings_submit($form, &$form_state) {
  if ($form_state['values']['date_format_short'] == 'custom') {
    $form_state['values']['date_format_short'] = $form_state['values']['date_format_short_custom'];
  }
  if ($form_state['values']['date_format_medium'] == 'custom') {
    $form_state['values']['date_format_medium'] = $form_state['values']['date_format_medium_custom'];
  }
  if ($form_state['values']['date_format_long'] == 'custom') {
    $form_state['values']['date_format_long'] = $form_state['values']['date_format_long_custom'];
  }
  return system_settings_form_submit($form, $form_state);
}

/**
 * Return the date for a given format string via Ajax.
 */
function system_date_time_lookup() {
  $result = format_date(time(), 'custom', $_GET['format']);
1650
  drupal_json($result);
1651 1652 1653 1654
}

/**
 * Form builder; Configure the site's maintenance status.
1655
 *
1656
 * @ingroup forms
1657
 * @see system_settings_form()
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680
 */
function system_site_maintenance_settings() {

  $form['site_offline'] = array(
    '#type' => 'radios',
    '#title' => t('Site status'),
    '#default_value' => variable_get('site_offline', 0),
    '#options' => array(t('Online'), t('Off-line')),
    '#description' => t('When set to "Online", all visitors will be able to browse your site normally. When set to "Off-line", only users with the "administer site configuration" permission will be able to access your site to perform maintenance; all other visitors will see the site off-line message configured below. Authorized users can log in during "Off-line" mode directly via the <a href="@user-login">user login</a> page.', array('@user-login' => url('user'))),
  );

  $form['site_offline_message'] = array(
    '#type' => 'textarea',
    '#title' => t('Site off-line message'),
    '#default_value' => variable_get('site_offline_message', t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')))),
    '#description' => t('Message to show visitors when the site is in off-line mode.')
  );

  return system_settings_form($form);
}

/**
 * Form builder; Configure Clean URL settings.
1681
 *
1682
 * @ingroup forms
1683
 * @see system_settings_form()
1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704
 */
function system_clean_url_settings() {
  $form['clean_url'] = array(
    '#type' => 'radios',
    '#title' => t('Clean URLs'),
    '#default_value' => variable_get('clean_url', 0),
    '#options' => array(t('Disabled'), t('Enabled')),
    '#description' => t('This option makes Drupal emit "clean" URLs (i.e. without <code>?q=</code> in the URL).'),
  );

  if (!variable_get('clean_url', 0)) {
    if (strpos(request_uri(), '?q=') !== FALSE) {
      drupal_add_js(drupal_get_path('module', 'system') .'/system.js', 'module');

      $form['clean_url']['#description'] .= ' <span>'. t('Before enabling clean URLs, you must perform a test to determine if your server is properly configured. If you are able to see this page again after clicking the "Run the clean URL test" link, the test has succeeded and the radio buttons above will be available. If instead you are directed to a "Page not found" error, you will need to change the configuration of your server. The <a href="@handbook">handbook page on Clean URLs</a> has additional troubleshooting information.', array('@handbook' => 'http://drupal.org/node/15365')) .'</span>';

      $form['clean_url']['#disabled'] = TRUE;
      $form['clean_url']['#prefix'] = '<div id="clean-url">';
      $form['clean_url']['#suffix'] = '<p>'. t('<a href="@clean_url">Run the clean url test</a>.', array('@clean_url' => base_path() .'admin/settings/clean-urls')) .'</p></div>';
    }
    else {
1705
      $form['clean_url']['#description'] .= ' <div class="ok">'. t('Your server has been successfully tested to support this feature.') .'</div>';
1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722
    }
  }

  return system_settings_form($form);
}

/**
 * Menu callback: displays the site status report. Can also be used as a pure check.
 *
 * @param $check
 *   If true, only returns a boolean whether there are system status errors.
 */
function system_status($check = FALSE) {
  // Load .install files
  include_once './includes/install.inc';
  drupal_load_updates();

1723
  // Check run-time requirements and status information.
1724 1725 1726 1727 1728 1729
  $requirements = module_invoke_all('requirements', 'runtime');
  usort($requirements, '_system_sort_requirements');

  if ($check) {
    return drupal_requirements_severity($requirements) == REQUIREMENT_ERROR;
  }
1730 1731 1732
  // MySQL import might have set the uid of the anonymous user to autoincrement
  // value. Let's try fixing it. See http://drupal.org/node/204411
  db_query("UPDATE {users} SET uid = uid - uid WHERE name = '' AND pass = '' AND status = 0");
1733 1734 1735 1736 1737 1738 1739 1740 1741 1742

  return theme('status_report', $requirements);
}

/**
 * Menu callback: run cron manually.
 */
function system_run_cron() {
  // Run cron manually
  if (drupal_cron_run()) {
1743
    drupal_set_message(t('Cron ran successfully.'));
1744 1745
  }
  else {
1746
    drupal_set_message(t('Cron run failed.'), 'error');
1747 1748
  }

1749
  drupal_goto('admin/reports/status');