system.admin.inc 81.1 KB
Newer Older
1
<?php
2
// $Id$
3 4

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

/**
 * Menu callback; Provide the administration overview page.
11 12 13 14 15 16 17 18 19 20
 */
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.
21
  if (system_status(TRUE) && user_access('administer site configuration')) {
22
    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');
23
  }
24
  $blocks = array();
25
  if ($admin = db_query("SELECT menu_name, mlid FROM {menu_links} WHERE link_path = 'admin' AND module = 'system'")->fetchAssoc()) {
26 27 28 29
    $result = db_query("
      SELECT m.*, ml.*
      FROM {menu_links} ml
      INNER JOIN {menu_router} m ON ml.router_path = m.path
30 31
      WHERE ml.link_path != 'admin/help' AND menu_name = :menu_name AND ml.plid = :mlid AND hidden = 0", $admin, array('fetch' => PDO::FETCH_ASSOC));
    foreach ($result as $item) {
32 33 34 35 36 37
      _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.
38 39
      if (!empty($item['localized_options']['attributes']['title'])) {
        $item['description'] = $item['localized_options']['attributes']['title'];
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.
50
      $blocks[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' . $item['mlid']] = $block;
51 52
    }
  }
53 54 55 56 57 58 59
  if ($blocks) {
    ksort($blocks);
    return theme('admin_page', $blocks);
  }
  else {
    return t('You do not have any administrative items.');
  }
60 61 62 63 64 65
}


/**
 * Provide a single block from the administration menu as a page.
 * This function is often a destination for these blocks.
66
 * For example, 'admin/structure/types' needs to have a destination to be valid
67 68
 * 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 86 87 88

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

89
  $modules = system_get_module_data();
90
  $menu_items = array();
91
  $help_arg = module_exists('help') ? drupal_help_arg() : FALSE;
92 93 94 95 96 97 98 99 100 101 102 103 104

  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.
105
      if ($help_arg && module_invoke($module, 'help', "admin/help#$module", $help_arg)) {
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
        $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;
}

/**
 * Menu callback; displays a listing of all themes.
137
 *
138
 * @ingroup forms
139
 * @see system_themes_form_submit()
140 141
 */
function system_themes_form() {
142

143
  $themes = system_get_theme_data();
144 145
  uasort($themes, 'system_sort_modules_by_info_name');

146 147 148 149 150 151
  $status = array();
  $incompatible_core = array();
  $incompatible_php = array();

  foreach ($themes as $theme) {
    $screenshot = NULL;
152 153 154 155 156 157 158 159 160 161
    // 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) + array($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'])) {
162 163 164 165 166 167
        $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');

168
    $form[$theme->name]['screenshot'] = array('#markup' => $screenshot);
169 170 171 172
    $form[$theme->name]['info'] = array(
      '#type' => 'value',
      '#value' => $theme->info,
    );
173
    $options[$theme->name] = $theme->info['name'];
174

175
    if (!empty($theme->status) || $theme->name == variable_get('admin_theme', 0)) {
176
      $form[$theme->name]['operations'] = array('#markup' => l(t('configure'), 'admin/appearance/settings/' . $theme->name) );
177 178 179 180
    }
    else {
      // Dummy element for drupal_render. Cleaner than adding a check in the theme function.
      $form[$theme->name]['operations'] = array();
181 182 183 184 185
    }
    if (!empty($theme->status)) {
      $status[] = $theme->name;
    }
    else {
186
      // Ensure this theme is compatible with this version of core.
187
      // Require the 'content' region to make sure the main page
188 189
      // content has a common place in all themes.
      if (!isset($theme->info['core']) || ($theme->info['core'] != DRUPAL_CORE_COMPATIBILITY) || (!isset($theme->info['regions']['content']))) {
190 191 192 193 194 195 196 197 198 199
        $incompatible_core[] = $theme->name;
      }
      if (version_compare(phpversion(), $theme->info['php']) < 0) {
        $incompatible_php[$theme->name] = $theme->info['php'];
      }
    }
  }

  $form['status'] = array(
    '#type' => 'checkboxes',
200
    '#options' => array_fill_keys(array_keys($options), ''),
201 202 203 204 205 206
    '#default_value' => $status,
    '#incompatible_themes_core' => drupal_map_assoc($incompatible_core),
    '#incompatible_themes_php' => $incompatible_php,
  );
  $form['theme_default'] = array(
    '#type' => 'radios',
207
    '#options' => array_fill_keys(array_keys($options), ''),
208 209
    '#default_value' => variable_get('theme_default', 'garland'),
  );
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231

  // Administration theme settings.
  $form['admin_theme'] = array(
    '#type' => 'fieldset',
    '#title' => t('Administration theme'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['admin_theme']['admin_theme'] = array(
    '#type' => 'select',
    '#options' => array(0 => t('Default theme')) + $options,
    '#title' => t('Administration theme'),
    '#description' => t('Choose which theme the administration pages should display in. If you choose "Default theme" the administration pages will use the same theme as the rest of the site.'),
    '#default_value' => variable_get('admin_theme', 0),
  );
  $form['admin_theme']['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'),
  );

232 233 234 235
  $form['buttons']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save configuration'),
  );
236

237 238 239
  return $form;
}

240 241 242
/**
 * Process system_themes_form form submissions.
 */
243
function system_themes_form_submit($form, &$form_state) {
244
  drupal_clear_css_cache();
245 246 247 248 249 250 251 252

  // 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;
    }
  }
253 254 255 256
  db_update('system')
    ->fields(array('status' => 0))
    ->condition('type', 'theme')
    ->execute();
257 258 259 260 261 262 263

  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) {
          $new_theme_list[] = $key;
264 265 266 267 268
          db_update('system')
            ->fields(array('status' => 1))
            ->condition('type', 'theme')
            ->condition('name', $key)
            ->execute();
269 270 271
        }
      }
    }
272
    if ($form_state['values']['admin_theme'] && $form_state['values']['admin_theme'] != $form_state['values']['theme_default']) {
273 274
      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'),
275
        '%admin_theme' => $form_state['values']['admin_theme'],
276 277 278
        '%selected_theme' => $form_state['values']['theme_default'],
      )));
    }
279 280

    // Save the variables.
281
    variable_set('theme_default', $form_state['values']['theme_default']);
282 283
    variable_set('admin_theme', $form_state['values']['admin_theme']);
    variable_set('node_admin_theme', $form_state['values']['node_admin_theme']);
284 285 286 287
  }
  else {
    // Revert to defaults: only Garland is enabled.
    variable_del('theme_default');
288 289
    variable_del('admin_theme');
    variable_del('node_admin_theme');
290 291 292 293 294
    db_update('system')
      ->fields(array('status' => 1))
      ->condition('type', 'theme')
      ->condition('name', 'garland')
      ->execute();
295 296 297 298 299
    $new_theme_list = array('garland');
  }

  list_themes(TRUE);
  menu_rebuild();
300
  drupal_theme_rebuild();
301
  drupal_set_message(t('The configuration options have been saved.'));
302
  $form_state['redirect'] = 'admin/appearance';
303 304 305 306 307 308 309 310 311 312 313

  // 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.
314
 *
315 316 317 318
 * @param $key
 *   A theme name.
 * @return
 *   The form structure.
319
 * @ingroup forms
320
 * @see system_theme_settings_submit()
321 322 323 324 325 326 327 328
 */
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);
329
    $var = str_replace('/', '_', 'theme_' . $key . '_settings');
330
    $themes = system_get_theme_data();
331 332 333 334 335 336 337 338 339 340 341 342
    $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);
343
    $filename = ($key) ? str_replace('/', '_', $key) . '_logo.' . $parts['extension'] : 'logo.' . $parts['extension'];
344 345 346 347

    // 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.
348
    if ($filepath = file_unmanaged_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
349
      $_POST['default_logo'] = 0;
350
      $_POST['logo_path'] = $filepath;
351 352 353 354 355 356 357
      $_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);
358
    $filename = ($key) ? str_replace('/', '_', $key) . '_favicon.' . $parts['extension'] : 'favicon.' . $parts['extension'];
359 360 361 362

    // 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.
363
    if ($filepath = file_unmanaged_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
364
      $_POST['default_favicon'] = 0;
365
      $_POST['favicon_path'] = $filepath;
366 367 368 369 370 371
      $_POST['toggle_favicon'] = 1;
    }
  }

  // Toggle settings
  $toggles = array(
372 373 374 375 376 377 378 379 380 381
    'logo'                      => t('Logo'),
    'name'                      => t('Site name'),
    'slogan'                    => t('Site slogan'),
    'node_user_picture'         => t('User pictures in posts'),
    'comment_user_picture'      => t('User pictures in comments'),
    'comment_user_verification' => t('User verification status in comments'),
    'search'                    => t('Search box'),
    'favicon'                   => t('Shortcut icon'),
    'main_menu'                 => t('Main menu'),
    'secondary_menu'            => t('Secondary menu'),
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
  );

  // 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)) {
401
      $form['theme_settings']['toggle_' . $name] = array('#type' => 'checkbox', '#title' => $title, '#default_value' => $settings['toggle_' . $name]);
402
      // Disable checkboxes for features not supported in the current configuration.
403 404
      if (isset($disabled['toggle_' . $name])) {
        $form['theme_settings']['toggle_' . $name]['#disabled'] = TRUE;
405 406 407 408
      }
    }
  }

409
  if (!element_children($form['theme_settings'])) {
410 411 412 413
    // 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;
  }
414 415 416 417 418 419 420 421 422

  // 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'),
    );
423
    $form['logo']['default_logo'] = array(
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
      '#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'),
448
      '#description' => t("Your shortcut icon, or 'favicon', is displayed in the address bar and bookmarks of most browsers."),
449 450 451 452 453 454 455 456 457 458
    );
    $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'),
459
      '#default_value' => $settings['favicon_path'],
460 461 462 463 464 465 466 467 468 469 470
      '#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) {
471
    // Include the theme's theme-settings.php file
472
    $filename = DRUPAL_ROOT . '/' . str_replace("/$key.info", '', $themes[$key]->filename) . '/theme-settings.php';
473 474 475
    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'];
476
      $filename = DRUPAL_ROOT . '/' . str_replace("/$base.info", '', $themes[$base]->filename) . '/theme-settings.php';
477 478 479 480 481 482
    }
    if (file_exists($filename)) {
      require_once $filename;
    }

    // Call engine-specific settings.
483
    $function = $themes[$key]->prefix . '_engine_settings';
484
    if (function_exists($function)) {
485
      $group = $function($settings, $form);
486 487 488
      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);
489
      }
490 491
    }
    // Call theme-specific settings.
492
    $function = $key . '_settings';
493
    if (!function_exists($function)) {
494
      $function = $themes[$key]->prefix . '_settings';
495 496
    }
    if (function_exists($function)) {
497
      $group = $function($settings, $form);
498 499 500
      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);
501 502 503 504
      }
    }
  }

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

511 512 513
/**
 * Process system_theme_settings form submissions.
 */
514
function system_theme_settings_submit($form, &$form_state) {
515
  $values = $form_state['values'];
516 517 518 519 520 521 522
  if (empty($values['default_favicon']) && !empty($values['favicon_path'])) {
    $mimetype = file_get_mimetype($values['favicon_path']);
    // Use the default MIME type for favicons if no other was found.
    if ($mimetype != 'application/octet-stream') {
      $values['favicon_mimetype'] = $mimetype;
    }
  }
523
  $key = $values['var'];
524

525 526 527 528
  // 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.'));
529 530 531 532

  cache_clear_all();
}

533
/**
534 535 536
 * Recursively check compatibility.
 *
 * @param $incompatible
537 538
 *   An associative array which at the end of the check contains all
 *   incompatible files as the keys, their values being TRUE.
539 540 541 542 543
 * @param $files
 *   The set of files that will be tested.
 * @param $file
 *   The file at which the check starts.
 * @return
544 545
 *   Returns TRUE if an incompatible file is found, NULL (no return value)
 *   otherwise.
546 547 548 549 550
 */
function _system_is_incompatible(&$incompatible, $files, $file) {
  if (isset($incompatible[$file->name])) {
    return TRUE;
  }
551 552 553
  // Recursively traverse required modules, looking for incompatible modules.
  foreach ($file->requires as $requires) {
    if (isset($files[$requires]) && _system_is_incompatible($incompatible, $files, $files[$requires])) {
554 555 556 557 558 559
      $incompatible[$file->name] = TRUE;
      return TRUE;
    }
  }
}

560 561 562 563
/**
 * Menu callback; provides module enable/disable interface.
 *
 * The list of modules gets populated by module.info files, which contain each module's name,
564
 * description and information about which modules it requires.
565 566
 * @see drupal_parse_info_file for information on module.info descriptors.
 *
567 568 569
 * Dependency checking is performed to ensure that a module:
 * - can not be enabled if there are disabled modules it requires.
 * - can not be disabled if there are enabled modules which depend on it.
570
 *
571 572
 * @param $form_state
 *   An associative array containing the current state of the form.
573
 * @ingroup forms
574 575
 * @see theme_system_modules()
 * @see system_modules_submit()
576 577 578 579 580
 * @return
 *   The form array.
 */
function system_modules($form_state = array()) {
  // Get current list of modules.
581
  $files = system_get_module_data();
582

583 584
  // Remove hidden modules from display list.
  foreach ($files as $filename => $file) {
585
    if (!empty($file->info['hidden']) || !empty($file->info['required'])) {
586 587 588 589
      unset($files[$filename]);
    }
  }

590 591
  uasort($files, 'system_sort_modules_by_info_name');

592 593 594 595
  // If the modules form was submitted, then system_modules_submit() runs first
  // and if there are unfilled required modules, then form_state['storage'] is
  // filled, triggering a rebuild. In this case we need to display a
  // confirmation form.
596 597 598
  if (!empty($form_state['storage'])) {
    return system_modules_confirm_form($files, $form_state['storage']);
  }
599

600 601
  $modules = array();
  $form['modules'] = array('#tree' => TRUE);
602

603 604 605
  // Used when checking if module implements a help page.
  $help_arg = module_exists('help') ? drupal_help_arg() : FALSE;

606 607 608 609
  // Iterate through each of the modules.
  foreach ($files as $filename => $module) {
    $extra = array();
    $extra['enabled'] = (bool) $module->status;
610 611 612 613
    // If this module requires other modules, add them to the array.
    foreach ($module->requires as $requires => $v) {
      if (!isset($files[$requires])) {
        $extra['requires'][$requires] = t('@module (<span class="admin-missing">missing</span>)', array('@module' => drupal_ucfirst($requires)));
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
        $extra['disabled'] = TRUE;
      }
      else {
        $requires_name = $files[$requires]->info['name'];
        if ($v) {
          $requires_name .= $v['original_version'];
          $current_version = str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $files[$requires]->info['version']);
          foreach ($v['versions'] as $required_version) {
            if ((isset($required_version['op']) && !version_compare($current_version, $required_version['version'], $required_version['op'])) ||
                (isset($required_version['preg']) && !preg_match($required_version['preg'], $current_version))) {
              $extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> version @version)', array(
                '@module' => $requires_name,
                '@version' => $files[$requires]->info['version'],
              ));
              $extra['disabled'] = TRUE;
            }
          }
631
        }
632 633 634 635 636 637 638
        if (!isset($extra['requires'][$requires])) {
          if ($files[$requires]->status) {
            $extra['requires'][$requires] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $requires_name));
          }
          else {
            $extra['requires'][$requires] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $requires_name));
          }
639 640
        }
      }
641 642
    }
    // Generate link for module's help page, if there is one.
643
    if ($help_arg && $module->status && in_array($filename, module_implements('help'))) {
644 645 646
      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"));
647 648
      }
    }
649
    // Mark dependents disabled so the user cannot remove required modules.
650
    $dependents = array();
651 652 653
    // If this module is required by other modules, list those, and then make it
    // impossible to disable this one.
    foreach ($module->required_by as $required_by => $v) {
654
      // Hidden modules are unset already.
655 656 657
      if (isset($files[$required_by])) {
        if ($files[$required_by]->status == 1) {
          $extra['required_by'][] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $files[$required_by]->info['name']));
658 659 660
          $extra['disabled'] = TRUE;
        }
        else {
661
          $extra['required_by'][] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $files[$required_by]->info['name']));
662
        }
663 664
      }
    }
665
    $form['modules'][$module->info['package']][$filename] = _system_modules_build_row($module->info, $extra);
666
  }
667 668 669 670 671 672 673 674 675 676 677 678 679 680
  // Add basic information to the fieldsets.
  foreach (element_children($form['modules']) as $package) {
    $form['modules'][$package] += array(
      '#type' => 'fieldset',
      '#title' => t($package),
      '#collapsible' => TRUE,
      '#theme' => 'system_modules_fieldset',
      '#header' => array(
        array('data' => t('Enabled'), 'class' => 'checkbox'),
        t('Name'),
        t('Version'),
        t('Description'),
      ),
    );
681 682
  }

683
  $form['submit'] = array(
684 685 686
    '#type' => 'submit',
    '#value' => t('Save configuration'),
  );
687
  $form['#action'] = url('admin/structure/modules/list/confirm');
688 689 690 691

  return $form;
}

692
/**
693
 * Array sorting callback; sorts modules or themes by their name.
694
 */
695 696 697 698
function system_sort_modules_by_info_name($a, $b) {
  return strcasecmp($a->info['name'], $b->info['name']);
}

699
/**
700
 * Build a table row for the system modules page.
701
 */
702 703 704
function _system_modules_build_row($info, $extra) {
  // Add in the defaults.
  $extra += array(
705 706
    'requires' => array(),
    'required_by' => array(),
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
    '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'],
  );
724 725
  $form['#requires'] = $extra['requires'];
  $form['#required_by'] = $extra['required_by'];
726 727 728 729 730 731 732

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

  // Check the core compatibility.
733
  if (!isset($info['core']) || $info['core'] != DRUPAL_CORE_COMPATIBILITY || empty($info['files'])) {
734 735 736 737 738 739 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
    $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'),
      '#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),
    );
765
    $form['description']['#markup'] .= theme('system_modules_incompatible', $status_long);
766 767 768 769 770 771 772
  }

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

/**
778
 * Display confirmation form for required modules.
779 780
 *
 * @param $modules
781
 *   Array of module file objects as returned from system_get_module_data().
782 783
 * @param $storage
 *   The contents of $form_state['storage']; an array with two
784
 *   elements: the list of required modules and the list of status
785 786 787 788 789 790 791 792 793
 *   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;
794

795
  foreach ($storage['more_modules'] as $info) {
796
    $t_argument = array(
797
      '@module' => $info['name'],
798
      '@required' => implode(', ', $info['requires']),
799
    );
800
    $items[] = format_plural(count($info['requires']), 'You must enable the @required module to install @module.', 'You must enable the @required modules to install @module.', $t_argument);
801
  }
802
  $form['text'] = array('#markup' => theme('item_list', $items));
803 804 805 806 807 808

  if ($form) {
    // Set some default form values
    $form = confirm_form(
      $form,
      t('Some required modules must be enabled'),
809
      'admin/structure/modules',
810 811 812 813 814 815 816 817 818 819 820
      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) {
821
  include_once DRUPAL_ROOT . '/includes/install.inc';
822 823
  $modules = array();
  // If we're not coming from the confirmation form, build the list of modules.
824
  if (!isset($form_state['storage'])) {
825 826 827
    foreach ($form_state['values']['modules'] as $group_name => $group) {
      foreach ($group as $module => $enabled) {
        $modules[$module] = array('group' => $group_name, 'enabled' => $enabled['enable']);
828 829 830 831
      }
    }
  }
  else {
832 833 834
    // If we are coming from the confirmation form, fetch
    // the modules out of $form_state.
    $modules = $form_state['storage']['modules'];
835 836
  }

837 838
  // Get a list of all modules, it will be used to find which module requires
  // which.
839
  $files = system_get_module_data();
840 841

  // The modules to be enabled.
842
  $modules_to_be_enabled = array();
843 844 845 846
  // The modules to be disabled.
  $disable_modules = array();
  // The modules to be installed.
  $new_modules = array();
847 848 849 850 851
  // Modules that need to be switched on because other modules require them.
  $more_modules = array();
  // Go through each module, finding out if we should enable, install, or
  // disable it. Also, we find out if there are modules it requires that are
  // not enabled.
852 853 854 855 856 857 858
  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;
      }
859 860
      elseif (!module_exists($name)) {
        $modules_to_be_enabled[$name] = $name;
861
      }
862 863 864
      // If we're not coming from a confirmation form, search for modules the
      // new ones require and see whether there are any that additionally
      // need to be switched on.
865
      if (empty($form_state['storage'])) {
866 867 868 869
        foreach ($form['modules'][$module['group']][$name]['#requires'] as $requires => $v) {
          if (!$modules[$requires]['enabled']) {
            if (!isset($more_modules[$name])) {
              $more_modules[$name]['name'] = $files[$name]->info['name'];
870
            }
871
            $more_modules[$name]['requires'][$requires] = $files[$requires]->info['name'];
872
          }
873
          $modules[$requires] = array('group' => $files[$requires]->info['package'], 'enabled' => TRUE);
874 875 876
        }
      }
    }
877 878 879 880 881
  }
  // A second loop is necessary, otherwise the modules set to be enabled in the
  // previous loop would not be found.
  foreach ($modules as $name => $module) {
    if (module_exists($name) && !$module['enabled']) {
882 883 884
      $disable_modules[$name] = $name;
    }
  }
885 886 887 888
  if ($more_modules) {
    // If we need to switch on more modules because other modules require
    // them and they haven't confirmed, don't process the submission yet. Store
    // the form submission data needed later.
889
    if (!isset($form_state['values']['confirm'])) {
890
      $form_state['storage'] = array('more_modules' => $more_modules, 'modules' => $modules);
891 892
      return;
    }
893
    // Otherwise, install or enable the modules.
894
    else {
895 896
      foreach ($form_state['storage']['more_modules'] as $info) {
        foreach ($info['requires'] as $requires => $name) {
897 898 899 900
          if (drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) {
            $new_modules[$name] = $name;
          }
          else {
901
            $modules_to_be_enabled[$name] = $name;
902 903 904
          }
        }
      }
905 906
    }
  }
907 908 909
  // Now we have installed every module as required (either by the user or
  // because other modules require them) so we don't need the temporary
  // storage anymore.
910 911 912 913
  unset($form_state['storage']);

  $old_module_list = module_list();

914
  // Enable the modules needing enabling.
915
  if (!empty($modules_to_be_enabled)) {
916 917 918 919 920
    $sort = array();
    foreach ($modules_to_be_enabled as $module) {
      $sort[$module] = $files[$module]->sort;
    }
    array_multisort($sort, $modules_to_be_enabled);
921
    module_enable($modules_to_be_enabled);
922
  }
923
  // Disable the modules that need disabling.
924
  if (!empty($disable_modules)) {
925 926 927 928 929
    $sort = array();
    foreach ($disable_modules as $module) {
      $sort[$module] = $files[$module]->sort;
    }
    array_multisort($sort, $disable_modules);
930 931 932 933
    module_disable($disable_modules);
  }

  // Install new modules.
934
  if (!empty($new_modules)) {
935
    $sort = array();
936 937 938 939
    foreach ($new_modules as $key => $module) {
      if (!drupal_check_module($module)) {
        unset($new_modules[$key]);
      }
940
      $sort[$module] = $files[$module]->sort;
941
    }
942
    array_multisort($sort, $new_modules);
943
    drupal_install_modules($new_modules);
944 945
  }

946
  $current_module_list = module_list(TRUE);
947 948 949 950
  if ($old_module_list != $current_module_list) {
    drupal_set_message(t('The configuration options have been saved.'));
  }

951 952 953 954 955
  // Clear all caches.
  registry_rebuild();
  drupal_theme_rebuild();
  node_types_rebuild();
  cache_clear_all('schema', 'cache');
956 957 958
  drupal_clear_css_cache();
  drupal_clear_js_cache();

959
  $form_state['redirect'] = 'admin/structure/modules';
960 961 962 963 964 965

  // 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);

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

969 970 971 972 973 974 975 976 977 978 979
  return;
}

/**
 * Uninstall functions
 */

/**
 * Builds a form of currently disabled modules.
 *
 * @ingroup forms
980 981
 * @see system_modules_uninstall_validate()
 * @see system_modules_uninstall_submit()
982 983
 * @param $form_state['values']
 *   Submitted form values.
984 985 986 987 988
 * @return
 *   A form array representing the currently disabled modules.
 */
function system_modules_uninstall($form_state = NULL) {
  // Make sure the install API is available.
989
  include_once DRUPAL_ROOT . '/includes/install.inc';
990 991 992 993 994 995 996 997 998

  // 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.
999 1000
  $disabled_modules = db_query("SELECT name, filename, info FROM {system} WHERE type = 'module' AND status = 0 AND schema_version > :schema ORDER BY name", array(':schema' => SCHEMA_UNINSTALLED));
  foreach ($disabled_modules as $module) {
1001 1002 1003 1004 1005 1006 1007 1008

    // 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')) {
1009 1010
      $form['modules'][$module->name]['name'] = array('#markup' => $info['name'] ? $info['name'] : $module->name);
      $form['modules'][$module->name]['description'] = array('#markup' => t($info['description']));
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
      $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'),
    );
1025
    $form['#action'] = url('admin/structure/modules/uninstall/confirm');
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
  }
  else {
    $form['modules'] = array();
  }

  return $form;
}

/**
 * Confirm uninstall of selected modules.
 *
 * @ingroup forms
1038 1039
 * @param $storage
 *   An associative array of modules selected to be uninstalled.
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
 * @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) {
1051
    $info = drupal_parse_info_file(dirname(drupal_get_filename('module', $module)) . '/' . $module . '.info');
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
    $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;
1062
    $form['modules'] = array('#markup' => '<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));
1063 1064 1065
    $form = confirm_form(
      $form,
      t('Confirm uninstall'),
1066
      'admin/structure/modules/uninstall',
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
      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');
1081
    drupal_goto('admin/structure/modules/uninstall');
1082 1083 1084 1085 1086 1087 1088 1089
  }
}

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

  if (!empty($form['#confirmed'])) {
    // Call the uninstall routine for each selected module.
1094 1095
    $modules = array_keys($form_state['values']['uninstall']);
    drupal_uninstall_modules($modules);
1096 1097 1098
    drupal_set_message(t('The selected modules have been uninstalled.'));

    unset($form_state['storage']);
1099
    $form_state['redirect'] = 'admin/structure/modules/uninstall';
1100 1101 1102 1103 1104 1105
  }
  else {
    $form_state['storage'] = $form_state['values'];
  }
}

1106 1107 1108 1109 1110 1111 1112 1113
/**
 * Menu callback. Display blocked IP addresses.
 */
function system_ip_blocking() {
  $output = '';
  $rows = array();
  $header = array(t('IP address'), t('Operations'));
  $result = db_query('SELECT * FROM {blocked_ips}');
1114
  foreach ($result as $ip) {
1115 1116 1117 1118 1119 1120
    $rows[] = array(
      $ip->ip,
      l(t('delete'), "admin/settings/ip-blocking/delete/$ip->iid"),
    );
  }

1121
  $build['system_ip_blocking_form'] = drupal_get_form('system_ip_blocking_form');
1122

1123 1124 1125 1126 1127
  $build['system_ip_blocking_table'] = array(
    '#theme' => 'table', 
    '#header' => $header, 
    '#rows' => $rows,
  );
1128

1129
  return $build;
1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
}

/**
 * Define the form for blocking IP addresses.
 *
 * @ingroup forms
 * @see system_ip_blocking_form_validate()
 * @see system_ip_blocking_form_submit()
 */
function system_ip_blocking_form($form_state) {
  $form['ip'] = array(
    '#title' => t('IP address'),
    '#type' => 'textfield',
    '#size' => 64,
    '#maxlength' => 32,
1145
    '#default_value' => arg(3),
1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
    '#description' => t('Enter a valid IP address.'),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  );
  $form['#submit'][] = 'system_ip_blocking_form_submit';
  $form['#validate'][] = 'system_ip_blocking_form_validate';
  return $form;
}

function system_ip_blocking_form_validate($form, &$form_state) {
1158
  $ip = trim($form_state['values']['ip']);
1159
  if (db_query("SELECT * FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField()) {
1160 1161
    form_set_error('ip', t('This IP address is already blocked.'));
  }
1162
  elseif ($ip == ip_address()) {
1163 1164
    form_set_error('ip', t('You may not block your own IP address.'));
  }
1165
  elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) {
1166 1167 1168 1169 1170
    form_set_error('ip', t('Please enter a valid IP address.'));
  }
}

function system_ip_blocking_form_submit($form, &$form_state) {
1171
  $ip = trim($form_state['values']['ip']);
1172 1173 1174
  db_insert('blocked_ips')
    ->fields(array('ip' => $ip))
    ->execute();
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
  drupal_set_message(t('The IP address %ip has been blocked.', array('%ip' => $ip)));
  $form_state['redirect'] = 'admin/settings/ip-blocking';
  return;
}

/**
 * IP deletion confirm page.
 *
 * @see system_ip_blocking_delete_submit()
 */
function system_ip_blocking_delete(&$form_state, $iid) {
  $form['blocked_ip'] = array(
    '#type' => 'value',
    '#value' => $iid,
  );
  return confirm_form($form, t('Are you sure you want to delete %ip?', array('%ip' => $iid['ip'])), 'admin/settings/ip-blocking', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
}

/**
 * Process system_ip_blocking_delete form submissions.
 */
function system_ip_blocking_delete_submit($form, &$form_state) {
  $blocked_ip = $form_state['values']['blocked_ip'];
1198 1199 1200
  db_delete('blocked_ips')
    ->condition('iid', $blocked_ip['iid'])
    ->execute();
1201 1202 1203 1204 1205
  watchdog('user', 'Deleted %ip', array('%ip' => $blocked_ip['ip']));
  drupal_set_message(t('The IP address %ip was deleted.', array('%ip' => $blocked_ip['ip'])));
  $form_state['redirect'] = 'admin/settings/ip-blocking';
}

1206 1207
/**
 * Form builder; The general site information form.
1208
 *
1209
 * @ingroup forms
1210
 * @see system_settings_form()
1211 1212 1213 1214
 */
function system_site_information_settings() {
  $form['site_name'] = array(
    '#type' => 'textfield',
1215
    '#title' => t('Site name'),
1216
    '#default_value' => 'Drupal',
1217 1218 1219 1220 1221
    '#required' => TRUE
  );
  $form['site_mail'] = array(
    '#type' => 'textfield',
    '#title' => t('E-mail address'),
1222
    '#default_value' => ini_get('sendmail_from'),
1223
    '#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.)"),
1224 1225 1226 1227 1228
    '#required' => TRUE,
  );
  $form['site_slogan'] = array(
    '#type' => 'textfield',
    '#title' => t('Slogan'),
1229
    '#default_value' => '',
1230 1231 1232 1233
  );
  $form['site_frontpage'] = array(
    '#type' => 'textfield',
    '#title' => t('Default front page'),
1234
    '#default_value' => 'node',
1235 1236
    '#size' => 40,
    '#description' => t('The home page displays content from this relative URL. If unsure, specify "node".'),
1237 1238
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
    '#required' => TRUE,
1239
  );
1240 1241 1242 1243
  $form['default_nodes_main'] = array(
    '#type' => 'select', '#title' => t('Number of posts on front page'),
    '#default_value' => 10,
    '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)),
1244
    '#description' => t('The maximum number of posts displayed on overview pages like the frontpage.')
1245
  );
1246 1247 1248 1249 1250
  $form['site_403'] = array(
    '#type' => 'textfield',
    '#title' => t('Default 403 (access denied) page'),
    '#default_value' => '',
    '#size' => 40,
1251
    '#description' => t('This page is displayed when the requested document is denied to the current user. Leave blank to display a generic "access denied" page.'),
1252 1253 1254 1255 1256 1257 1258
    '#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'),
    '#default_value' => '',
    '#size' => 40,
1259
    '#description' => t('This page is displayed when no other content matches the requested document. Leave blank to display a generic "page not found" page.'),
1260 1261
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=')
  );
1262 1263 1264 1265 1266 1267
  $form['site_404_blocks'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display blocks on default 404 (not found) page'),
    '#description' => t('Render all blocks on the default 404 (not found) page. Disabling blocks can help with performance but might leave users with a less functional site.'),
    '#default_value' => variable_get('site_404_blocks', 0)
  );
1268
  $form['#validate'][] = 'system_site_information_settings_validate';
1269

1270
  return system_settings_form($form);
1271 1272
}

1273 1274 1275 1276 1277 1278 1279 1280 1281
/**
 * 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.
1282 1283
  $item = array('link_path' => drupal_get_normal_path($form_state['values']['site_frontpage']));
  if (!menu_valid_path($item)) {
1284 1285 1286 1287
    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'])));
  }
}

1288 1289
/**
 * Form builder; Configure error reporting settings.
1290
 *
1291
 * @ingroup forms
1292
 * @see system_settings_form()
1293
 */