Code coverage for /20080809/modules/system/system.admin.inc

Line #Times calledCode
1
<?php
2
// $Id: system.admin.inc,v 1.84 2008/08/09 12:41:23 dries Exp $
3
4
/**
5
 * @file
6
 * Admin page callbacks for the system module.
7
 */
8
9
/**
10
 * Menu callback; Provide the administration overview page.
11
 */
12125
function system_main_admin_page($arg = NULL) {
13
  // If we received an argument, they probably meant some other page.
14
  // Let's 404 them since the menu system cannot be told we do not
15
  // accept arguments.
162
  if (isset($arg) && substr($arg, 0, 3) != 'by-') {
170
    return drupal_not_found();
180
  }
19
20
  // Check for status report errors.
212
  if (system_status(TRUE) && user_access('administer site configuration'))
{
220
    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');
230
  }
242
  $blocks = array();
252
  if ($admin = db_fetch_array(db_query("SELECT menu_name, mlid FROM
{menu_links} WHERE link_path = 'admin' AND module = 'system'"))) {
262
    $result = db_query("
27
      SELECT m.*, ml.*
28
      FROM {menu_links} ml
29
      INNER JOIN {menu_router} m ON ml.router_path = m.path
302
      WHERE ml.link_path != 'admin/help' AND menu_name = '%s' AND ml.plid =
%d AND hidden = 0", $admin);
312
    while ($item = db_fetch_array($result)) {
322
      _menu_link_translate($item);
332
      if (!$item['access']) {
341
        continue;
350
      }
36
      // The link 'description' either derived from the hook_menu
'description'
37
      // or entered by the user via menu module is saved as the title
attribute.
382
      if (!empty($item['localized_options']['attributes']['title'])) {
392
        $item['description'] =
$item['localized_options']['attributes']['title'];
402
      }
412
      $block = $item;
422
      $block['content'] = '';
432
      if ($item['block_callback'] &&
function_exists($item['block_callback'])) {
440
        $function = $item['block_callback'];
450
        $block['content'] .= $function();
460
      }
472
      $block['content'] .= theme('admin_block_content',
system_admin_menu_block($item));
48
      // Prepare for sorting as in function _menu_tree_check_access().
49
      // The weight is offset so it is always positive, with a uniform
5-digits.
502
      $blocks[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' .
$item['mlid']] = $block;
512
    }
522
  }
532
  if ($blocks) {
542
    ksort($blocks);
552
    return theme('admin_page', $blocks);
560
  }
57
  else {
580
    return t('You do not have any administrative items.');
59
  }
600
}
61
62
63
/**
64
 * Provide a single block from the administration menu as a page.
65
 * This function is often a destination for these blocks.
66
 * For example, 'admin/build/types' needs to have a destination to be
valid
67
 * in the Drupal menu system, but too much information there might be
68
 * hidden, so we supply the contents of the block.
69
 *
70
 * @return
71
 *   The output HTML.
72
 */
73125
function system_admin_menu_block_page() {
740
  $item = menu_get_item();
750
  if ($content = system_admin_menu_block($item)) {
760
    $output = theme('admin_block_content', $content);
770
  }
78
  else {
790
    $output = t('You do not have any administrative items.');
80
  }
810
  return $output;
820
}
83
84
/**
85
 * Menu callback; prints a listing of admin tasks for each installed
module.
86
 */
87125
function system_admin_by_module() {
88
892
  $modules = module_rebuild_cache();
902
  $menu_items = array();
912
  $help_arg = module_exists('help') ? drupal_help_arg() : FALSE;
92
932
  foreach ($modules as $file) {
942
    $module = $file->name;
952
    if ($module == 'help') {
962
      continue;
970
    }
98
992
    $admin_tasks = system_get_module_admin_tasks($module);
100
101
    // Only display a section if there are any available tasks.
1022
    if (count($admin_tasks)) {
103
104
      // Check for help links.
1051
      if ($help_arg && module_invoke($module, 'help', "admin/help#$module",
$help_arg)) {
1061
        $admin_tasks[100] = l(t('Get help'), "admin/help/$module");
1071
      }
108
109
      // Sort.
1101
      ksort($admin_tasks);
111
1121
      $menu_items[$file->info['name']] = array($file->info['description'],
$admin_tasks);
1131
    }
1142
  }
1152
  return theme('system_admin_by_module', $menu_items);
1160
}
117
118
/**
119
 * Menu callback; displays a module's settings page.
120
 */
121125
function system_settings_overview() {
122
  // Check database setup if necessary
1230
  if (function_exists('db_check_setup') && empty($_POST)) {
1240
    db_check_setup();
1250
  }
126
1270
  $item = menu_get_item('admin/settings');
1280
  $content = system_admin_menu_block($item);
129
1300
  $output = theme('admin_block_content', $content);
131
1320
  return $output;
1330
}
134
135
/**
136
 * Form builder; This function allows selection of the theme to show in
administration sections.
137
 *
138
 * @ingroup forms
139
 * @see system_settings_form()
140
 */
141125
function system_admin_theme_settings() {
1420
  $themes = system_theme_data();
143
1440
  uasort($themes, 'system_sort_modules_by_info_name');
145
1460
  $options[0] = '<' . t('System default') . '>';
1470
  foreach ($themes as $theme) {
1480
    $options[$theme->name] = $theme->info['name'];
1490
  }
150
1510
  $form['admin_theme'] = array(
1520
    '#type' => 'select',
1530
    '#options' => $options,
1540
    '#title' => t('Administration theme'),
1550
    '#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.'),
1560
    '#default_value' => variable_get('admin_theme', '0'),
157
  );
158
1590
  $form['node_admin_theme'] = array(
1600
    '#type' => 'checkbox',
1610
    '#title' => t('Use administration theme for content editing'),
1620
    '#description' => t('Use the administration theme when editing existing
posts or creating new ones.'),
1630
    '#default_value' => variable_get('node_admin_theme', '0'),
164
  );
165
1660
  $form['#submit'][] = 'system_admin_theme_submit';
1670
  return system_settings_form($form);
1680
}
169
170
/**
171
 * Menu callback; displays a listing of all themes.
172
 *
173
 * @ingroup forms
174
 * @see system_themes_form_submit()
175
 */
176125
function system_themes_form() {
177
1780
  drupal_clear_css_cache();
1790
  $themes = system_theme_data();
180
1810
  uasort($themes, 'system_sort_modules_by_info_name');
182
1830
  $status = array();
1840
  $incompatible_core = array();
1850
  $incompatible_php = array();
186
1870
  foreach ($themes as $theme) {
1880
    $screenshot = NULL;
1890
    $theme_key = $theme->name;
1900
    while ($theme_key) {
1910
      if (file_exists($themes[$theme_key]->info['screenshot'])) {
1920
        $screenshot = $themes[$theme_key]->info['screenshot'];
1930
        break;
1940
      }
1950
      $theme_key = isset($themes[$theme_key]->info['base theme']) ?
$themes[$theme_key]->info['base theme'] : NULL;
1960
    }
1970
    $screenshot = $screenshot ? theme('image', $screenshot, t('Screenshot
for %theme theme', array('%theme' => $theme->info['name'])), '',
array('class' => 'screenshot'), FALSE) : t('no screenshot');
198
1990
    $form[$theme->name]['screenshot'] = array('#markup' => $screenshot);
2000
    $form[$theme->name]['info'] = array(
2010
      '#type' => 'value',
2020
      '#value' => $theme->info,
203
    );
2040
    $options[$theme->name] = '';
205
2060
    if (!empty($theme->status) || $theme->name ==
variable_get('admin_theme', '0')) {
2070
      $form[$theme->name]['operations'] = array('#markup' =>
l(t('configure'), 'admin/build/themes/settings/' . $theme->name) );
2080
    }
209
    else {
210
      // Dummy element for drupal_render. Cleaner than adding a check in
the theme function.
2110
      $form[$theme->name]['operations'] = array();
212
    }
2130
    if (!empty($theme->status)) {
2140
      $status[] = $theme->name;
2150
    }
216
    else {
217
      // Ensure this theme is compatible with this version of core.
2180
      if (!isset($theme->info['core']) || $theme->info['core'] !=
DRUPAL_CORE_COMPATIBILITY) {
2190
        $incompatible_core[] = $theme->name;
2200
      }
2210
      if (version_compare(phpversion(), $theme->info['php']) < 0) {
2220
        $incompatible_php[$theme->name] = $theme->info['php'];
2230
      }
224
    }
2250
  }
226
2270
  $form['status'] = array(
2280
    '#type' => 'checkboxes',
2290
    '#options' => $options,
2300
    '#default_value' => $status,
2310
    '#incompatible_themes_core' => drupal_map_assoc($incompatible_core),
2320
    '#incompatible_themes_php' => $incompatible_php,
233
  );
2340
  $form['theme_default'] = array(
2350
    '#type' => 'radios',
2360
    '#options' => $options,
2370
    '#default_value' => variable_get('theme_default', 'garland'),
238
  );
2390
  $form['buttons']['submit'] = array(
2400
    '#type' => 'submit',
2410
    '#value' => t('Save configuration'),
242
  );
2430
  $form['buttons']['reset'] = array(
2440
    '#type' => 'submit',
2450
    '#value' => t('Reset to defaults'),
246
  );
2470
  return $form;
2480
}
249
250
/**
251
 * Process system_themes_form form submissions.
252
 */
253125
function system_themes_form_submit($form, &$form_state) {
254
255
  // Store list of previously enabled themes and disable all themes
2560
  $old_theme_list = $new_theme_list = array();
2570
  foreach (list_themes() as $theme) {
2580
    if ($theme->status) {
2590
      $old_theme_list[] = $theme->name;
2600
    }
2610
  }
2620
  db_query("UPDATE {system} SET status = 0 WHERE type = 'theme'");
263
2640
  if ($form_state['values']['op'] == t('Save configuration')) {
2650
    if (is_array($form_state['values']['status'])) {
2660
      foreach ($form_state['values']['status'] as $key => $choice) {
267
        // Always enable the default theme, despite its status checkbox
being checked:
2680
        if ($choice || $form_state['values']['theme_default'] == $key) {
2690
          system_initialize_theme_blocks($key);
2700
          $new_theme_list[] = $key;
2710
          db_query("UPDATE {system} SET status = 1 WHERE type = 'theme' and
name = '%s'", $key);
2720
        }
2730
      }
2740
    }
2750
    if (($admin_theme = variable_get('admin_theme', '0')) != '0' &&
$admin_theme != $form_state['values']['theme_default']) {
2760
      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(
2770
        '!admin_theme_page' => url('admin/settings/admin'),
2780
        '%admin_theme' => $admin_theme,
2790
        '%selected_theme' => $form_state['values']['theme_default'],
2800
      )));
2810
    }
2820
    variable_set('theme_default', $form_state['values']['theme_default']);
2830
  }
284
  else {
285
    // Revert to defaults: only Garland is enabled.
2860
    variable_del('theme_default');
2870
    db_query("UPDATE {system} SET status = 1 WHERE type = 'theme' AND name
= 'garland'");
2880
    $new_theme_list = array('garland');
289
  }
290
2910
  list_themes(TRUE);
2920
  menu_rebuild();
2930
  drupal_theme_rebuild();
2940
  drupal_set_message(t('The configuration options have been saved.'));
2950
  $form_state['redirect'] = 'admin/build/themes';
296
297
  // Notify locale module about new themes being enabled, so translations
can
298
  // be imported. This might start a batch, and only return to the
redirect
299
  // path after that.
3000
  module_invoke('locale', 'system_update', array_diff($new_theme_list,
$old_theme_list));
301
3020
  return;
3030
}
304
305
/**
306
 * Form builder; display theme configuration for entire site and individual
themes.
307
 *
308
 * @param $key
309
 *   A theme name.
310
 * @return
311
 *   The form structure.
312
 * @ingroup forms
313
 * @see system_theme_settings_submit()
314
 */
315125
function system_theme_settings(&$form_state, $key = '') {
3160
  $directory_path = file_directory_path();
3170
  file_check_directory($directory_path, FILE_CREATE_DIRECTORY,
'file_directory_path');
318
319
  // Default settings are defined in theme_get_settings() in
includes/theme.inc
3200
  if ($key) {
3210
    $settings = theme_get_settings($key);
3220
    $var = str_replace('/', '_', 'theme_' . $key . '_settings');
3230
    $themes = system_theme_data();
3240
    $features = $themes[$key]->info['features'];
3250
  }
326
  else {
3270
    $settings = theme_get_settings('');
3280
    $var = 'theme_settings';
329
  }
330
3310
  $form['var'] = array('#type' => 'hidden', '#value' => $var);
332
333
  // Check for a new uploaded logo, and use that instead.
3340
  if ($file = file_save_upload('logo_upload',
array('file_validate_is_image' => array()))) {
3350
    $parts = pathinfo($file->filename);
3360
    $filename = ($key) ? str_replace('/', '_', $key) . '_logo.' .
$parts['extension'] : 'logo.' . $parts['extension'];
337
338
    // The image was saved using file_save_upload() and was added to the
339
    // files table as a temporary file. We'll make a copy and let the
garbage
340
    // collector delete the original upload.
3410
    if (file_copy($file, $filename, FILE_EXISTS_REPLACE)) {
3420
      $_POST['default_logo'] = 0;
3430
      $_POST['logo_path'] = $file->filepath;
3440
      $_POST['toggle_logo'] = 1;
3450
    }
3460
  }
347
348
  // Check for a new uploaded favicon, and use that instead.
3490
  if ($file = file_save_upload('favicon_upload')) {
3500
    $parts = pathinfo($file->filename);
3510
    $filename = ($key) ? str_replace('/', '_', $key) . '_favicon.' .
$parts['extension'] : 'favicon.' . $parts['extension'];
352
353
    // The image was saved using file_save_upload() and was added to the
354
    // files table as a temporary file. We'll make a copy and let the
garbage
355
    // collector delete the original upload.
3560
    if (file_copy($file, $filename)) {
3570
      $_POST['default_favicon'] = 0;
3580
      $_POST['favicon_path'] = $file->filepath;
3590
      $_POST['toggle_favicon'] = 1;
3600
    }
3610
  }
362
363
  // Toggle settings
364
  $toggles = array(
3650
    'logo'                 => t('Logo'),
3660
    'name'                 => t('Site name'),
3670
    'slogan'               => t('Site slogan'),
3680
    'mission'              => t('Mission statement'),
3690
    'node_user_picture'    => t('User pictures in posts'),
3700
    'comment_user_picture' => t('User pictures in comments'),
3710
    'search'               => t('Search box'),
3720
    'favicon'              => t('Shortcut icon'),
3730
    'main_menu'            => t('Main menu'),
3740
    'secondary_menu'      => t('Secondary menu'),
3750
  );
376
377
  // Some features are not always available
3780
  $disabled = array();
3790
  if (!variable_get('user_pictures', 0)) {
3800
    $disabled['toggle_node_user_picture'] = TRUE;
3810
    $disabled['toggle_comment_user_picture'] = TRUE;
3820
  }
3830
  if (!module_exists('search')) {
3840
    $disabled['toggle_search'] = TRUE;
3850
  }
386
3870
  $form['theme_settings'] = array(
3880
    '#type' => 'fieldset',
3890
    '#title' => t('Toggle display'),
3900
    '#description' => t('Enable or disable the display of certain page
elements.'),
391
  );
3920
  foreach ($toggles as $name => $title) {
3930
    if ((!$key) || in_array($name, $features)) {
3940
      $form['theme_settings']['toggle_' . $name] = array('#type' =>
'checkbox', '#title' => $title, '#default_value' => $settings['toggle_' .
$name]);
395
      // Disable checkboxes for features not supported in the current
configuration.
3960
      if (isset($disabled['toggle_' . $name])) {
3970
        $form['theme_settings']['toggle_' . $name]['#disabled'] = TRUE;
3980
      }
3990
    }
4000
  }
401
402
  // System wide only settings.
4030
  if (!$key) {
404
    // Create neat 2-column layout for the toggles
4050
    $form['theme_settings'] += array(
4060
      '#prefix' => '<div class="theme-settings-left">',
4070
      '#suffix' => '</div>',
408
    );
409
410
    // Toggle node display.
4110
    $node_types = node_get_types('names');
4120
    if ($node_types) {
4130
      $form['node_info'] = array(
4140
        '#type' => 'fieldset',
4150
        '#title' => t('Display post information on'),
4160
        '#description' => t('Enable or disable the <em>submitted by
Username on date</em> text when displaying posts of the following type.'),
4170
        '#prefix' => '<div class="theme-settings-right">',
4180
        '#suffix' => '</div>',
419
      );
4200
      foreach ($node_types as $type => $name) {
4210
        $form['node_info']["toggle_node_info_$type"] = array('#type' =>
'checkbox', '#title' => check_plain($name), '#default_value' =>
$settings["toggle_node_info_$type"]);
4220
      }
4230
    }
4240
  }
4250
  elseif (!element_children($form['theme_settings'])) {
426
    // If there is no element in the theme settings fieldset then do not
show
427
    // it -- but keep it in the form if another module wants to alter.
4280
    $form['theme_settings']['#access'] = FALSE;
4290
  }
430
431
  // Logo settings
4320
  if ((!$key) || in_array('logo', $features)) {
4330
    $form['logo'] = array(
4340
      '#type' => 'fieldset',
4350
      '#title' => t('Logo image settings'),
4360
      '#description' => t('If toggled on, the following logo will be
displayed.'),
4370
      '#attributes' => array('class' => 'theme-settings-bottom'),
438
    );
4390
    $form['logo']["default_logo"] = array(
4400
      '#type' => 'checkbox',
4410
      '#title' => t('Use the default logo'),
4420
      '#default_value' => $settings['default_logo'],
4430
      '#tree' => FALSE,
4440
      '#description' => t('Check here if you want the theme to use the logo
supplied with it.')
4450
    );
4460
    $form['logo']['logo_path'] = array(
4470
      '#type' => 'textfield',
4480
      '#title' => t('Path to custom logo'),
4490
      '#default_value' => $settings['logo_path'],
4500
      '#description' => t('The path to the file you would like to use as
your logo file instead of the default logo.'));
451
4520
    $form['logo']['logo_upload'] = array(
4530
      '#type' => 'file',
4540
      '#title' => t('Upload logo image'),
4550
      '#maxlength' => 40,
4560
      '#description' => t("If you don't have direct file access to the
server, use this field to upload your logo.")
4570
    );
4580
  }
459
4600
  if ((!$key) || in_array('favicon', $features)) {
4610
    $form['favicon'] = array(
4620
      '#type' => 'fieldset',
4630
      '#title' => t('Shortcut icon settings'),
4640
      '#description' => t("Your shortcut icon, or 'favicon', is displayed
in the address bar and bookmarks of most browsers.")
4650
    );
4660
    $form['favicon']['default_favicon'] = array(
4670
      '#type' => 'checkbox',
4680
      '#title' => t('Use the default shortcut icon.'),
4690
      '#default_value' => $settings['default_favicon'],
4700
      '#description' => t('Check here if you want the theme to use the
default shortcut icon.')
4710
    );
4720
    $form['favicon']['favicon_path'] = array(
4730
      '#type' => 'textfield',
4740
      '#title' => t('Path to custom icon'),
4750
      '#default_value' => $settings['favicon_path'],
4760
      '#description' => t('The path to the image file you would like to use
as your custom shortcut icon.')
4770
    );
478
4790
    $form['favicon']['favicon_upload'] = array(
4800
      '#type' => 'file',
4810
      '#title' => t('Upload icon image'),
4820
      '#description' => t("If you don't have direct file access to the
server, use this field to upload your shortcut icon.")
4830
    );
4840
  }
485
4860
  if ($key) {
487
    // Include the theme's theme-settings.php file
4880
    $filename = './' . str_replace("/$key.info", '',
$themes[$key]->filename) . '/theme-settings.php';
4890
    if (!file_exists($filename) and !empty($themes[$key]->info['base
theme'])) {
490
      // If the theme doesn't have a theme-settings.php file, use the base
theme's.
4910
      $base = $themes[$key]->info['base theme'];
4920
      $filename = './' . str_replace("/$base.info", '',
$themes[$base]->filename) . '/theme-settings.php';
4930
    }
4940
    if (file_exists($filename)) {
4950
      require_once $filename;
4960
    }
497
498
    // Call engine-specific settings.
4990
    $function = $themes[$key]->prefix . '_engine_settings';
5000
    if (function_exists($function)) {
5010
      $group = $function($settings);
5020
      if (!empty($group)) {
5030
        $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)));
5040
        $form['engine_specific'] = array_merge($form['engine_specific'],
$group);
5050
      }
5060
    }
507
    // Call theme-specific settings.
5080
    $function = $key . '_settings';
5090
    if (!function_exists($function)) {
5100
      $function = $themes[$key]->prefix . '_settings';
5110
    }
5120
    if (function_exists($function)) {
5130
      $group = $function($settings);
5140
      if (!empty($group)) {
5150
        $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'])));
5160
        $form['theme_specific'] = array_merge($form['theme_specific'],
$group);
5170
      }
5180
    }
5190
  }
5200
  $form['#attributes'] = array('enctype' => 'multipart/form-data');
521
5220
  $form = system_settings_form($form);
523
  // We don't want to call system_settings_form_submit(), so change
#submit.
5240
  $form['#submit'] = array('system_theme_settings_submit');
5250
  return $form;
5260
}
527
528
/**
529
 * Process system_theme_settings form submissions.
530
 */
531125
function system_theme_settings_submit($form, &$form_state) {
5320
  $values = $form_state['values'];
5330
  $key = $values['var'];
534
5350
  if ($values['op'] == t('Reset to defaults')) {
5360
    variable_del($key);
5370
    drupal_set_message(t('The configuration options have been reset to
their default values.'));
5380
  }
539
  else {
540
    // Exclude unnecessary elements before saving.
5410
    unset($values['var'], $values['submit'], $values['reset'],
$values['form_id'], $values['op'], $values['form_build_id'],
$values['form_token']);
5420
    variable_set($key, $values);
5430
    drupal_set_message(t('The configuration options have been saved.'));
544
  }
545
5460
  cache_clear_all();
5470
}
548
549
/**
550
 * Recursively check compatibility.
551
 *
552
 * @param $incompatible
553
 *   An associative array which at the end of the check contains all
incompatible files as the keys, their values being TRUE.
554
 * @param $files
555
 *   The set of files that will be tested.
556
 * @param $file
557
 *   The file at which the check starts.
558
 * @return
559
 *   Returns TRUE if an incompatible file is found, NULL (no return value)
otherwise.
560
 */
561125
function _system_is_incompatible(&$incompatible, $files, $file) {
5620
  static $seen;
563
  // We need to protect ourselves in case of a circular dependency.
5640
  if (isset($seen[$file->name])) {
5650
    return isset($incompatible[$file->name]);
5660
  }
5670
  $seen[$file->name] = TRUE;
5680
  if (isset($incompatible[$file->name])) {
5690
    return TRUE;
5700
  }
571
  // The 'dependencies' key in .info files was a string in Drupal 5, but
changed
572
  // to an array in Drupal 6. If it is not an array, the module is not
573
  // compatible and we can skip the check below which requires an array.
5740
  if (!is_array($file->info['dependencies'])) {
5750
    $file->info['dependencies'] = array();
5760
    $incompatible[$file->name] = TRUE;
5770
    return TRUE;
5780
  }
579
  // Recursively traverse the dependencies, looking for incompatible
modules
5800
  foreach ($file->info['dependencies'] as $dependency) {
5810
    if (isset($files[$dependency]) &&
_system_is_incompatible($incompatible, $files, $files[$dependency])) {
5820
      $incompatible[$file->name] = TRUE;
5830
      return TRUE;
5840
    }
5850
  }
5860
}
587
588
/**
589
 * Menu callback; provides module enable/disable interface.
590
 *
591
 * The list of modules gets populated by module.info files, which contain
each module's name,
592
 * description and dependencies.
593
 * @see drupal_parse_info_file for information on module.info descriptors.
594
 *
595
 * Dependency checking is performed to ensure that a module cannot be
enabled if the module has
596
 * disabled dependencies and also to ensure that the module cannot be
disabled if the module has
597
 * enabled dependents.
598
 *
599
 * @param $form_state
600
 *   An associative array containing the current state of the form.
601
 * @ingroup forms
602
 * @see theme_system_modules()
603
 * @see system_modules_submit()
604
 * @return
605
 *   The form array.
606
 */
607125
function system_modules($form_state = array()) {
608
  // Clear all caches.
6099
  drupal_theme_rebuild();
6109
  node_types_rebuild();
6119
  menu_rebuild();
6129
  cache_clear_all('schema', 'cache');
613
614
  // Get current list of modules.
6159
  $files = module_rebuild_cache();
616
617
  // Remove hidden modules from display list.
6189
  foreach ($files as $filename => $file) {
6199
    if (!empty($file->info['hidden'])) {
6209
      unset($files[$filename]);
6219
    }
6229
  }
623
6249
  uasort($files, 'system_sort_modules_by_info_name');
625
6269
  if (!empty($form_state['storage'])) {
6271
    return system_modules_confirm_form($files, $form_state['storage']);
6280
  }
6299
  $dependencies = array();
6309
  $modules = array();
6319
  $form['modules'] = array('#tree' => TRUE);
632
633
  // Used when checking if module implements a help page.
6349
  $help_arg = module_exists('help') ? drupal_help_arg() : FALSE;
635
636
  // The list of required modules.
6379
  $modules_required = drupal_required_modules();
638
639
  // Iterate through each of the modules.
6409
  foreach ($files as $filename => $module) {
6419
    $extra = array();
642
    // If the module is requried, set it to be so.
6439
    if (in_array($filename, $modules_required)) {
6449
      $extra['required'] = TRUE;
6459
    }
6469
    $extra['enabled'] = (bool) $module->status;
647
    // If this module has dependencies, add them to the array.
6489
    if (is_array($module->info['dependencies'])) {
6499
      foreach ($module->info['dependencies'] as $dependency) {
6509
        if (!isset($files[$dependency])) {
6510
          $extra['dependencies'][$dependency] = drupal_ucfirst($dependency)
. t(' (<span class="admin-missing">missing</span>)');
6520
          $extra['disabled'] = TRUE;
6530
        }
6549
        elseif (!$files[$dependency]->status) {
6559
          $extra['dependencies'][$dependency] =
$files[$dependency]->info['name'] . t(' (<span
class="admin-disabled">disabled</span>)');
6569
        }
657
        else {
6589
          $extra['dependencies'][$dependency] =
$files[$dependency]->info['name'] . t(' (<span
class="admin-enabled">enabled</span>)');
659
        }
6609
      }
6619
    }
662
    // Generate link for module's help page, if there is one.
6639
    if ($help_arg && module_hook($filename, 'help')) {
6649
      if (module_invoke($filename, 'help', "admin/help#$filename",
$help_arg)) {
665
        // Module has a help page.
6669
        $extra['help'] = theme('more_help_link',
url("admin/help/$filename"));
6679
      }
6689
    }
669
    // Mark dependents disabled so user can not remove modules being
depended on.
6709
    $dependents = array();
6719
    foreach ($module->info['dependents'] as $dependent) {
6729
      if ($files[$dependent]->status == 1) {
6731
        $extra['dependents'][] = $files[$dependent]->info['name'] . t('
(<span class="admin-enabled">enabled</span>)');
6741
        $extra['disabled'] = TRUE;
6751
      }
676
      else {
6779
        $extra['dependents'][] = $files[$dependent]->info['name'] . t('
(<span class="admin-disabled">disabled</span>)');
678
      }
6799
    }
6809
    $form['modules'][$module->info['package']][$filename] =
_system_modules_build_row($module->info, $extra);
6819
  }
682
  // Add basic information to the fieldsets.
6839
  foreach (element_children($form['modules']) as $package) {
6840
    $form['modules'][$package] += array(
6859
      '#type' => 'fieldset',
6869
      '#title' => t($package),
6879
      '#collapsible' => TRUE,
6889
      '#collapsed' => ($package == 'Core - required'),
6899
      '#theme' => 'system_modules_fieldset',
690
      '#header' => array(
6919
        array('data' => t('Enabled'), 'class' => 'checkbox'),
6929
        t('Name'),
6939
        t('Version'),
6949
        t('Description'),
6959
      ),
696
    );
6979
  }
698
6999
  $form['submit'] = array(
7009
    '#type' => 'submit',
7019
    '#value' => t('Save configuration'),
702
  );
7039
  $form['#action'] = url('admin/build/modules/list/confirm');
704
7059
  return $form;
7060
}
707
708
/**
709
 * Array sorting callback; sorts modules or themes by their name.
710
 */
711125
function system_sort_modules_by_info_name($a, $b) {
7129
  return strcasecmp($a->info['name'], $b->info['name']);
7130
}
714
715
/**
716
 * Build a table row for the system modules page.
717
 */
718125
function _system_modules_build_row($info, $extra) {
719
  // Add in the defaults.
720
  $extra += array(
7219
    'required' => FALSE,
7229
    'dependencies' => array(),
7239
    'dependents' => array(),
7249
    'disabled' => FALSE,
7259
    'enabled' => FALSE,
7269
    'help' => '',
7270
  );
728
  $form = array(
7299
    '#tree' => TRUE,
7309
  );
731
  // Set the basic properties.
7329
  $form['name'] = array(
7339
    '#markup' => t($info['name']),
734
  );
7359
  $form['description'] = array(
7369
    '#markup' => t($info['description']),
737
  );
7389
  $form['version'] = array(
7399
    '#markup' => $info['version'],
740
  );
7419
  $form['#dependencies'] = $extra['dependencies'];
7429
  $form['#dependents'] = $extra['dependents'];
743
744
  // Check the compatibilities.
7459
  $compatible = TRUE;
7469
  $status_short = '';
7479
  $status_long = '';
748
749
  // Check the core compatibility.
7509
  if (!isset($info['core']) || $info['core'] != DRUPAL_CORE_COMPATIBILITY)
{
7510
    $compatible = FALSE;
7520
    $status_short .= t('Incompatible with this version of Drupal core. ');
7530
    $status_long .= t('This version is incompatible with the !core_version
version of Drupal core. ', array('!core_version' => VERSION));
7540
  }
755
756
  // Ensure this module is compatible with the currently installed version
of PHP.
7579
  if (version_compare(phpversion(), $info['php']) < 0) {
7580
    $compatible = FALSE;
7590
    $status_short .= t('Incompatible with this version of PHP');
7600
    if (substr_count($info['php'], '.') < 2) {
7610
      $php_required .= '.*';
7620
    }
7630
    $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()));
7640
  }
765
766
  // If this module is compatible, present a checkbox indicating
767
  // this module may be installed. Otherwise, show a big red X.
7689
  if ($compatible) {
7699
    $form['enable'] = array(
7709
      '#type' => 'checkbox',
7719
      '#title' => t('Enable'),
7729
      '#required' => $extra['required'],
7739
      '#default_value' => $extra['enabled'],
774
    );
7759
    if ($extra['disabled']) {
7761
      $form['enable']['#disabled'] = TRUE;
7771
    }
7789
  }
779
  else {
7800
    $form['enable'] = array(
7810
      '#markup' =>  theme('image', 'misc/watchdog-error.png',
t('incompatible'), $status_short),
782
    );
7830
    $form['description']['#value'] .= theme('system_modules_incompatible',
$status_long);
784
  }
785
786
  // Show a "more help" link for modules that have them.
7879
  if ($extra['help']) {
7889
    $form['help'] = array(
7899
      '#markup' => $extra['help'],
790
    );
7919
  }
7929
  return $form;
7930
}
794
795
/**
796
 * Display confirmation form for dependencies.
797
 *
798
 * @param $modules
799
 *   Array of module file objects as returned from module_rebuild_cache().
800
 * @param $storage
801
 *   The contents of $form_state['storage']; an array with two
802
 *   elements: the list of dependencies and the list of status
803
 *   form field values from the previous screen.
804
 * @ingroup forms
805
 */
806125
function system_modules_confirm_form($modules, $storage) {
8071
  $form = array();
8081
  $items = array();
809
8101
  $form['validation_modules'] = array('#type' => 'value', '#value' =>
$modules);
8111
  $form['status']['#tree'] = TRUE;
812
8131
  foreach ($storage['dependencies'] as $info) {
814
    $t_argument = array(
8151
      '@module' => $info['name'],
8161
      '@dependencies' => implode(', ', $info['dependencies']),
8171
    );
8181
    $items[] = format_plural(count($info['dependencies']), 'You must enable
the @dependencies module to install @module.', 'You must enable the
@dependencies modules to install @module.', $t_argument);
8191
  }
8201
  $form['text'] = array('#markup' => theme('item_list', $items));
821
8221
  if ($form) {
823
    // Set some default form values
8241
    $form = confirm_form(
8251
      $form,
8261
      t('Some required modules must be enabled'),
8271
      'admin/build/modules',
8281
      t('Would you like to continue with enabling the above?'),
8291
      t('Continue'),
8301
      t('Cancel'));
8311
    return $form;
8320
  }
8330
}
834
835
/**
836
 * Submit callback; handles modules form submission.
837
 */
838125
function system_modules_submit($form, &$form_state) {
8394
  include_once './includes/install.inc';
8404
  $modules = array();
841
  // If we're not coming from the confirmation form, build the list of
modules.
8424
  if (!isset($form_state['storage'])) {
8433
    foreach ($form_state['values']['modules'] as $group_name => $group) {
8443
      foreach ($group as $module => $enabled) {
8453
        $modules[$module] = array('group' => $group_name, 'enabled' =>
$enabled['enable']);
8463
      }
8473
    }
8483
  }
849
  else {
850
    // If we are coming from the confirmation form, fetch
851
    // the modules out of $form_state.
8521
    $modules = $form_state['storage']['modules'];
853
  }
854
855
  // Get a list of all modules, for building dependencies with.
8564
  $files = module_rebuild_cache();
857
858
  // The modules to be enabled.
8594
  $enabled_modules = array();
860
  // The modules to be disabled.
8614
  $disable_modules = array();
862
  // The modules to be installed.
8634
  $new_modules = array();
864
  // The un-met dependencies.
8654
  $dependencies = array();
866
  // Go through each module, finding out
867
  // if we should enable, install, or disable it,
868
  // and if it has any un-met dependencies.
8694
  foreach ($modules as $name => $module) {
870
    // If it's enabled, find out whether to just
871
    // enable it, or install it.
8724
    if ($module['enabled']) {
8734
      if (drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED)
{
8743
        $new_modules[$name] = $name;
8753
      }
876
      else {
8774
        $enable_modules[$name] = $name;
878
      }
879
      // If we're not coming from a confirmation form,
880
      // search dependencies. Otherwise, the user will have already
881
      // approved of the depdent modules being enabled.
8824
      if (empty($form_state['storage'])) {
8833
        foreach ($form['modules'][$module['group']][$name]['#dependencies']
as $dependency => $string) {
8841
          if (!isset($dependencies[$name])) {
8851
            $dependencies[$name] = array(
8861
              'name' => $files[$name]->info['name'],
8871
              'dependencies' => array($dependency =>
$files[$dependency]->info['name']),
888
            );
8891
          }
890
          else {
8910
            $dependencies[$name]['dependencies'][$dependency] =
$files[$dependency]->info['name'];
892
          }
8931
          $modules[$dependency] = array('group' =>
$files[$dependency]->info['package'], 'enabled' => TRUE);
8941
        }
8953
      }
8964
    }
897
    else {
8984
      $disable_modules[$name] = $name;
899
    }
9004
  }
9014
  if ($dependencies) {
902
    // If there where un-met dependencies and they haven't confirmed don't
process
903
    // the submission yet. Store the form submission data needed later.
9041
    if (!isset($form_state['values']['confirm'])) {
9051
      $form_state['storage'] = array('dependencies' => $dependencies,
'modules' => $modules);
9061
      return;
9070
    }
908
    // Otherwise, install or enable the modules.
909
    else {
9100
      $dependencies = $form_storage['dependencies'];
9110
      foreach ($dependencies as $info) {
9120
        foreach ($info['dependencies'] as $dependency => $name) {
9130
          if (drupal_get_installed_schema_version($name) ==
SCHEMA_UNINSTALLED) {
9140
            $new_modules[$name] = $name;
9150
          }
916
          else {
9170
            $enable_modules[$name] = $name;
918
          }
9190
        }
9200
      }
921
    }
9220
  }
923
  // If we have no dependencies, or the dependencies are confirmed
924
  // to be installed, we don't need the temporary storage anymore.
9253
  unset($form_state['storage']);
926
9273
  $old_module_list = module_list();
928
929
  // Enable the modules needing enabling.
9303
  if (!empty($enable_modules)) {
9313
    module_enable($enable_modules);
9323
  }
933
  // Disable the modules that need disabling.
9343
  if (!empty($disable_modules)) {
9353
    module_disable($disable_modules);
9363
  }
937
938
  // Install new modules.
9393
  if (!empty($new_modules)) {
9402
    foreach ($new_modules as $key => $module) {
9412
      if (!drupal_check_module($module)) {
9420
        unset($new_modules[$key]);
9430
      }
9442
    }
9452
    drupal_install_modules($new_modules);
9462
  }
947
9483
  $current_module_list = module_list(TRUE, FALSE);
9493
  if ($old_module_list != $current_module_list) {
9503
    drupal_set_message(t('The configuration options have been saved.'));
9513
  }
952
9533
  drupal_clear_css_cache();
9543
  drupal_clear_js_cache();
955
9563
  $form_state['redirect'] = 'admin/build/modules';
957
958
  // Notify locale module about module changes, so translations can be
959
  // imported. This might start a batch, and only return to the redirect
960
  // path after that.
9613
  module_invoke('locale', 'system_update', $new_modules);
962
963
  // Synchronize to catch any actions that were added or removed.
9643
  actions_synchronize();
965
9663
  return;
9670
}
968
969
/**
970
 * Uninstall functions
971
 */
972
973
/**
974
 * Builds a form of currently disabled modules.
975
 *
976
 * @ingroup forms
977
 * @see system_modules_uninstall_validate()
978
 * @see system_modules_uninstall_submit()
979
 * @param $form_state['values']
980
 *   Submitted form values.
981
 * @return
982
 *   A form array representing the currently disabled modules.
983
 */
984125
function system_modules_uninstall($form_state = NULL) {
985
  // Make sure the install API is available.
9863
  include_once './includes/install.inc';
987
988
  // Display the confirm form if any modules have been submitted.
9893
  if (isset($form_state) && $confirm_form =
system_modules_uninstall_confirm_form($form_state['storage'])) {
9901
    return $confirm_form;
9910
  }
992
9933
  $form = array();
994
995
  // Pull all disabled modules from the system table.
9963
  $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);
9973
  while ($module = db_fetch_object($disabled_modules)) {
998
999
    // Grab the module info
10002
    $info = unserialize($module->info);
1001
1002
    // Load the .install file, and check for an uninstall hook.
1003
    // If the hook exists, the module can be uninstalled.
10042
    module_load_install($module->name);
10052
    if (module_hook($module->name, 'uninstall')) {
10062
      $form['modules'][$module->name]['name'] = array('#markup' =>
$info['name'] ? $info['name'] : $module->name);
10072
      $form['modules'][$module->name]['description'] = array('#markup' =>
t($info['description']));
10082
      $options[$module->name] = '';
10092
    }
10102
  }
1011
1012
  // Only build the rest of the form if there are any modules available to
uninstall.
10133
  if (!empty($options)) {
10142
    $form['uninstall'] = array(
10152
      '#type' => 'checkboxes',
10162
      '#options' => $options,
1017
    );
10182
    $form['buttons']['submit'] = array(
10192
      '#type' => 'submit',
10202
      '#value' => t('Uninstall'),
1021
    );
10222
    $form['#action'] = url('admin/build/modules/uninstall/confirm');
10232
  }
1024
  else {
10251
    $form['modules'] = array();
1026
  }
1027
10283
  return $form;
10290
}
1030
1031
/**
1032
 * Confirm uninstall of selected modules.
1033
 *
1034
 * @ingroup forms
1035
 * @param $storage
1036
 *   An associative array of modules selected to be uninstalled.
1037
 * @return
1038
 *   A form array representing modules to confirm.
1039
 */
1040125
function system_modules_uninstall_confirm_form($storage) {
1041
  // Nothing to build.
10423
  if (!isset($storage)) {
10433
    return;
10440
  }
1045
1046
  // Construct the hidden form elements and list items.
10471
  foreach (array_filter($storage['uninstall']) as $module => $value) {
10481
    $info = drupal_parse_info_file(dirname(drupal_get_filename('module',
$module)) . '/' . $module . '.info');
10491
    $uninstall[] = $info['name'];
10501
    $form['uninstall'][$module] = array('#type' => 'hidden',
10511
      '#value' => 1,
1052
    );
10531
  }
1054
1055
  // Display a confirm form if modules have been selected.
10561
  if (isset($uninstall)) {
10571
    $form['#confirmed'] = TRUE;
10581
    $form['uninstall']['#tree'] = TRUE;
10591
    $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));
10601
    $form = confirm_form(
10611
      $form,
10621
      t('Confirm uninstall'),
10631
      'admin/build/modules/uninstall',
10641
      t('Would you like to continue with uninstalling the above?'),
10651
      t('Uninstall'),
10661
      t('Cancel'));
10671
    return $form;
10680
  }
10690
}
1070
1071
/**
1072
 * Validates the submitted uninstall form.
1073
 */
1074125
function system_modules_uninstall_validate($form, &$form_state) {
1075
  // Form submitted, but no modules selected.
10762
  if (!count(array_filter($form_state['values']['uninstall']))) {
10770
    drupal_set_message(t('No modules selected.'), 'error');
10780
    drupal_goto('admin/build/modules/uninstall');
10790
  }
10802
}
1081
1082
/**
1083
 * Processes the submitted uninstall form.
1084
 */
1085125
function system_modules_uninstall_submit($form, &$form_state) {
1086
  // Make sure the install API is available.
10872
  include_once './includes/install.inc';
1088
10892
  if (!empty($form['#confirmed'])) {
1090
    // Call the uninstall routine for each selected module.
10911
    foreach (array_filter($form_state['values']['uninstall']) as $module =>
$value) {
10921
      drupal_uninstall_module($module);
10931
    }
10941
    drupal_set_message(t('The selected modules have been uninstalled.'));
1095
10961
    unset($form_state['storage']);
10971
    $form_state['redirect'] = 'admin/build/modules/uninstall';
10981
  }
1099
  else {
11001
    $form_state['storage'] = $form_state['values'];
1101
  }
11022
}
1103
1104
/**
1105
 * Menu callback. Display blocked IP addresses.
1106
 */
1107125
function system_ip_blocking() {
110817
  $output = '';
110917
  $rows = array();
111017
  $header = array(t('IP address'), t('Operations'));
111117
  $result = db_query('SELECT * FROM {blocked_ips}');
111217
  while ($ip = db_fetch_object($result)) {
111310
    $rows[] = array(
111410
      $ip->ip,
111510
      l(t('delete'), "admin/settings/ip-blocking/delete/$ip->iid"),
1116
    );
111710
  }
1118
111917
  $output .= drupal_get_form('system_ip_blocking_form');
1120
112115
  $output .= theme('table', $header, $rows);
1122
112315
  return $output;
11240
}
1125
1126
/**
1127
 * Define the form for blocking IP addresses.
1128
 *
1129
 * @ingroup forms
1130
 * @see system_ip_blocking_form_validate()
1131
 * @see system_ip_blocking_form_submit()
1132
 */
1133125
function system_ip_blocking_form($form_state) {
113417
  $form['ip'] = array(
113517
    '#title' => t('IP address'),
113617
    '#type' => 'textfield',
113717
    '#size' => 64,
113817
    '#maxlength' => 32,
113917
    '#default_value' => arg(3),
114017
    '#description' => t('Enter a valid IP address.'),
1141
  );
114217
  $form['submit'] = array(
114317
    '#type' => 'submit',
114417
    '#value' => t('Save'),
1145
  );
114617
  $form['#submit'][] = 'system_ip_blocking_form_submit';
114717
  $form['#validate'][] = 'system_ip_blocking_form_validate';
114817
  return $form;
11490
}
1150
1151125
function system_ip_blocking_form_validate($form, &$form_state) {
11526
  $ip = trim($form_state['values']['ip']);
11536
  if (db_result(db_query("SELECT * FROM {blocked_ips} WHERE ip = '%s'",
$ip))) {
11541
    form_set_error('ip', t('This IP address is already blocked.'));
11551
  }
11565
  else if ($ip == ip_address()) {
11570
    form_set_error('ip', t('You may not block your own IP address.'));
11580
  }
11595
  else if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) ==
FALSE) {
11603
    form_set_error('ip', t('Please enter a valid IP address.'));
11613
  }
11626
}
1163
1164125
function system_ip_blocking_form_submit($form, &$form_state) {
11652
  $ip = trim($form_state['values']['ip']);
11662
  db_query("INSERT INTO {blocked_ips} (ip) VALUES ('%s')", $ip);
11672
  drupal_set_message(t('The IP address %ip has been blocked.', array('%ip'
=> $ip)));
11682
  $form_state['redirect'] = 'admin/settings/ip-blocking';
11692
  return;
11700
}
1171
1172
/**
1173
 * IP deletion confirm page.
1174
 *
1175
 * @see system_ip_blocking_delete_submit()
1176
 */
1177125
function system_ip_blocking_delete(&$form_state, $iid) {
11783
  $form['blocked_ip'] = array(
11793
    '#type' => 'value',
11803
    '#value' => $iid,
1181
  );
11823
  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'));
11830
}
1184
1185
/**
1186
 * Process system_ip_blocking_delete form submissions.
1187
 */
1188125
function system_ip_blocking_delete_submit($form, &$form_state) {
11891
  $blocked_ip = $form_state['values']['blocked_ip'];
11901
  db_query("DELETE FROM {blocked_ips} WHERE iid = %d",
$blocked_ip['iid']);
11911
  watchdog('user', 'Deleted %ip', array('%ip' => $blocked_ip['ip']));
11921
  drupal_set_message(t('The IP address %ip was deleted.', array('%ip' =>
$blocked_ip['ip'])));
11931
  $form_state['redirect'] = 'admin/settings/ip-blocking';
11941
}
1195
1196
/**
1197
 * Form builder; The general site information form.
1198
 *
1199
 * @ingroup forms
1200
 * @see system_settings_form()
1201
 */
1202125
function system_site_information_settings() {
12030
  $form['site_name'] = array(
12040
    '#type' => 'textfield',
12050
    '#title' => t('Name'),
12060
    '#default_value' => variable_get('site_name', 'Drupal'),
12070
    '#description' => t('The name of this website.'),
1208
    '#required' => TRUE
12090
  );
12100
  $form['site_mail'] = array(
12110
    '#type' => 'textfield',
12120
    '#title' => t('E-mail address'),
12130
    '#default_value' => variable_get('site_mail',
ini_get('sendmail_from')),
12140
    '#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.)"),
12150
    '#required' => TRUE,
1216
  );
12170
  $form['site_slogan'] = array(
12180
    '#type' => 'textfield',
12190
    '#title' => t('Slogan'),
12200
    '#default_value' => variable_get('site_slogan', ''),
12210
    '#description' => t("Your site's motto, tag line, or catchphrase (often
displayed alongside the title of the site).")
12220
  );
12230
  $form['site_mission'] = array(
12240
    '#type' => 'textarea',
12250
    '#title' => t('Mission'),
12260
    '#default_value' => variable_get('site_mission', ''),
12270
    '#description' => t("Your site's mission or focus statement (often
prominently displayed on the front page).")
12280
  );
12290
  $form['site_footer'] = array(
12300
    '#type' => 'textarea',
12310
    '#title' => t('Footer message'),
12320
    '#default_value' => variable_get('site_footer', ''),
12330
    '#description' => t('This text will be displayed at the bottom of each
page. Useful for adding a copyright notice to your pages.')
12340
  );
12350
  $form['anonymous'] = array(
12360
    '#type' => 'textfield',
12370
    '#title' => t('Anonymous user'),
12380
    '#default_value' => variable_get('anonymous', t('Anonymous')),
12390
    '#description' => t('The name used to indicate anonymous users.'),
12400
    '#required' => TRUE,
1241
  );
12420
  $form['site_frontpage'] = array(
12430
    '#type' => 'textfield',
12440
    '#title' => t('Default front page'),
12450
    '#default_value' => variable_get('site_frontpage', 'node'),
12460
    '#size' => 40,
12470
    '#description' => t('The home page displays content from this relative
URL. If unsure, specify "node".'),
12480
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) .
(variable_get('clean_url', 0) ? '' : '?q='),
12490
    '#required' => TRUE,
1250
  );
12510
  $form['#validate'][] = 'system_site_information_settings_validate';
1252
12530
  return system_settings_form($form);
12540
}
1255
1256
/**
1257
 * Validate the submitted site-information form.
1258
 */
1259125
function system_site_information_settings_validate($form, &$form_state) {
1260
  // Validate the e-mail address.
12610
  if ($error = user_validate_mail($form_state['values']['site_mail'])) {
12620
    form_set_error('site_mail', $error);
12630
  }
1264
  // Validate front page path.
12650
  $item = array('link_path' => $form_state['values']['site_frontpage']);
12660
  $normal_path = drupal_get_normal_path($item['link_path']);
12670
  if ($item['link_path'] != $normal_path) {
12680
    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)));
12690
    $item['link_path'] = $normal_path;
12700
  }
12710
  if (!empty($item) && !menu_valid_path($item)) {
12720
    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'])));
12730
  }
12740
}
1275
1276
/**
1277
 * Form builder; Configure error reporting settings.
1278
 *
1279
 * @ingroup forms
1280
 * @see system_settings_form()
1281
 */
1282125
function system_error_reporting_settings() {
1283
12840
  $form['site_403'] = array(
12850
    '#type' => 'textfield',
12860
    '#title' => t('Default 403 (access denied) page'),
12870
    '#default_value' => variable_get('site_403', ''),
12880
    '#size' => 40,
12890
    '#description' => t('This page is displayed when the requested document
is denied to the current user. If unsure, specify nothing.'),
12900
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) .
(variable_get('clean_url', 0) ? '' : '?q=')
12910
  );
1292
12930
  $form['site_404'] = array(
12940
    '#type' => 'textfield',
12950
    '#title' => t('Default 404 (not found) page'),
12960
    '#default_value' => variable_get('site_404', ''),
12970
    '#size' => 40,
12980
    '#description' => t('This page is displayed when no other content
matches the requested document. If unsure, specify nothing.'),
12990
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) .
(variable_get('clean_url', 0) ? '' : '?q=')
13000
  );
1301
13020
  $form['error_level'] = array(
13030
    '#type' => 'select', '#title' => t('Error reporting'), '#default_value'
=> variable_get('error_level', 1),
13040
    '#options' => array(t('Write errors to the log'), t('Write errors to
the log and to the screen')),
13050
    '#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.')
13060
  );
1307
13080
  return system_settings_form($form);
13090
}
1310
1311
/**
1312
 * Menu callback; Menu page for the various logging options.
1313
 */
1314125
function system_logging_overview() {
13150
  $item = menu_get_item('admin/settings/logging');
13160
  $content = system_admin_menu_block($item);
1317
13180
  $output = theme('admin_block_content', $content);
1319
13200
  return $output;
13210
}
1322
1323
/**
1324
 * Form builder; Configure site performance settings.
1325
 *
1326
 * @ingroup forms
1327
 * @see system_settings_form()
1328
 */
1329125
function system_performance_settings() {
1330
13310
  $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>';
1332
13330
  $problem_modules = array_unique(array_merge(module_implements('boot'),
module_implements('exit')));
13340
  sort($problem_modules);
1335
13360
  if (count($problem_modules) > 0) {
13370
    $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>';
13380
  }
1339
  else {
13400
    $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>';
1341
  }
13420
  $form['page_cache'] = array(
13430
    '#type' => 'fieldset',
13440
    '#title' => t('Page cache'),
13450
    '#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.'),
1346
  );
1347
13480
  $form['page_cache']['cache'] = array(
13490
    '#type' => 'radios',
13500
    '#title' => t('Caching mode'),
13510
    '#default_value' => variable_get('cache', CACHE_DISABLED),
13520
    '#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)')),
1353
    '#description' => $description
13540
  );
1355
13560
  $period = drupal_map_assoc(array(0, 60, 180, 300, 600, 900, 1800, 2700,
3600, 10800, 21600, 32400, 43200, 86400), 'format_interval');
13570
  $period[0] = '<' . t('none') . '>';
13580
  $form['page_cache']['cache_lifetime'] = array(
13590
    '#type' => 'select',
13600
    '#title' => t('Minimum cache lifetime'),
13610
    '#default_value' => variable_get('cache_lifetime', 0),
13620
    '#options' => $period,
13630
    '#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.')
13640
  );
13650
  $form['page_cache']['page_compression'] = array(
13660
    '#type' => 'radios',
13670
    '#title' => t('Page compression'),
13680
    '#default_value' => variable_get('page_compression', TRUE),
13690
    '#options' => array(t('Disabled'), t('Enabled')),
13700
    '#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."),
1371
  );
1372
13730
  $form['block_cache'] = array(
13740
    '#type' => 'fieldset',
13750
    '#title' => t('Block cache'),
13760
    '#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.'),
1377
  );
1378
13790
  $form['block_cache']['block_cache'] = array(
13800
    '#type' => 'radios',
13810
    '#title' => t('Block cache'),
13820
    '#default_value' => variable_get('block_cache', CACHE_DISABLED),
13830
    '#options' => array(CACHE_DISABLED => t('Disabled'), CACHE_NORMAL =>
t('Enabled (recommended)')),
13840
    '#disabled' => count(module_implements('node_grants')),
13850
    '#description' => t('Note that block caching is inactive when modules
defining content access restrictions are enabled.'),
1386
  );
1387
13880
  $form['bandwidth_optimizations'] = array(
13890
    '#type' => 'fieldset',
13900
    '#title' => t('Bandwidth optimizations'),
13910
    '#description' => '<p>' . t('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>' . t('These
options are disabled if you have not set up your files directory, or if
your download method is set to private.') . '</p>'
13920
  );
1393
13940
  $directory = file_directory_path();
13950
  $is_writable = is_dir($directory) && is_writable($directory) &&
(variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) ==
FILE_DOWNLOADS_PUBLIC);
13960
  $form['bandwidth_optimizations']['preprocess_css'] = array(
13970
    '#type' => 'radios',
13980
    '#title' => t('Optimize CSS files'),
13990
    '#default_value' => intval(variable_get('preprocess_css', 0) &&
$is_writable),
14000
    '#disabled' => !$is_writable,
14010
    '#options' => array(t('Disabled'), t('Enabled')),
14020
    '#description' => t('This option can interfere with theme development
and should only be enabled in a production environment.'),
1403
  );
14040
  $form['bandwidth_optimizations']['preprocess_js'] = array(
14050
    '#type' => 'radios',
14060
    '#title' => t('Optimize JavaScript files'),
14070
    '#default_value' => intval(variable_get('preprocess_js', 0) &&
$is_writable),
14080
    '#disabled' => !$is_writable,
14090
    '#options' => array(t('Disabled'), t('Enabled')),
14100
    '#description' => t('This option can interfere with module development
and should only be enabled in a production environment.'),
1411
  );
1412
14130
  $form['clear_cache'] = array(
14140
    '#type' => 'fieldset',
14150
    '#title' => t('Clear cached data'),
14160
    '#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>'),
1417
  );
1418
14190
  $form['clear_cache']['clear'] = array(
14200
    '#type' => 'submit',
14210
    '#value' => t('Clear cached data'),
14220
    '#submit' => array('system_clear_cache_submit'),
1423
  );
1424
14250
  $form['#submit'][] = 'drupal_clear_css_cache';
14260
  $form['#submit'][] = 'drupal_clear_js_cache';
1427
14280
  return system_settings_form($form);
14290
}
1430
1431
/**
1432
 * Submit callback; clear system caches.
1433
 *
1434
 * @ingroup forms
1435
 */
1436125
function system_clear_cache_submit(&$form_state, $form) {
14370
  drupal_flush_all_caches();
14380
  drupal_set_message(t('Caches cleared.'));
14390
}
1440
1441
/**
1442
 * Form builder; Configure the site file handling.
1443
 *
1444
 * @ingroup forms
1445
 * @see system_settings_form()
1446
 */
1447125
function system_file_system_settings() {
1448
14490
  $form['file_directory_path'] = array(
14500
    '#type' => 'textfield',
14510
    '#title' => t('File system path'),
14520
    '#default_value' => file_directory_path(),
14530
    '#maxlength' => 255,
14540
    '#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.'),
14550
    '#after_build' => array('system_check_directory'),
1456
  );
1457
14580
  $form['file_directory_temp'] = array(
14590
    '#type' => 'textfield',
14600
    '#title' => t('Temporary directory'),
14610
    '#default_value' => file_directory_temp(),
14620
    '#maxlength' => 255,
14630
    '#description' => t('A file system path where uploaded files will be
stored during previews.'),
14640
    '#after_build' => array('system_check_directory'),
1465
  );
1466
14670
  $form['file_downloads'] = array(
14680
    '#type' => 'radios',
14690
    '#title' => t('Download method'),
14700
    '#default_value' => variable_get('file_downloads',
FILE_DOWNLOADS_PUBLIC),
14710
    '#options' => array(FILE_DOWNLOADS_PUBLIC => t('Public - files are
available using HTTP directly.'), FILE_DOWNLOADS_PRIVATE => t('Private -
files are transferred by Drupal.')),
14720
    '#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.')
14730
  );
1474
14750
  return system_settings_form($form);
14760
}
1477
1478
/**
1479
 * Form builder; Configure site image toolkit usage.
1480
 *
1481
 * @ingroup forms
1482
 * @see system_settings_form()
1483
 */
1484125
function system_image_toolkit_settings() {
14850
  $toolkits_available = image_get_available_toolkits();
14860
  if (count($toolkits_available) > 1) {
14870
    $form['image_toolkit'] = array(
14880
      '#type' => 'radios',
14890
      '#title' => t('Select an image processing toolkit'),
14900
      '#default_value' => variable_get('image_toolkit',
image_get_toolkit()),
1491
      '#options' => $toolkits_available
14920
    );
14930
  }
14940
  elseif (count($toolkits_available) == 1) {
14950
    variable_set('image_toolkit', key($toolkits_available));
14960
  }
1497
14980
  $form['image_toolkit_settings'] = image_toolkit_invoke('settings');
1499
15000
  return system_settings_form($form);
15010
}
1502
1503
/**
1504
 * Form builder; Configure how the site handles RSS feeds.
1505
 *
1506
 * @ingroup forms
1507
 * @see system_settings_form()
1508
 */
1509125
function system_rss_feeds_settings() {
1510
15110
  $form['feed_default_items'] = array(
15120
    '#type' => 'select',
15130
    '#title' => t('Number of items in each feed'),
15140
    '#default_value' => variable_get('feed_default_items', 10),
15150
    '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15,
20, 25, 30)),
15160
    '#description' => t('Default number of items to include in each
feed.')
15170
  );
15180
  $form['feed_item_length'] = array(
15190
    '#type' => 'select',
15200
    '#title' => t('Feed content'),
15210
    '#default_value' => variable_get('feed_item_length', 'teaser'),
15220
    '#options' => array('title' => t('Titles only'), 'teaser' => t('Titles
plus teaser'), 'fulltext' => t('Full text')),
15230
    '#description' => t('Global setting for the default display of content
items in each feed.')
15240
  );
1525
15260
  return system_settings_form($form);
15270
}
1528
1529
/**
1530
 * Form builder; Configure the site date and time settings.
1531
 *
1532
 * @ingroup forms
1533
 * @see system_settings_form()
1534
 * @see system_date_time_settings_submit()
1535
 */
1536125
function system_date_time_settings() {
15370
  drupal_add_js(drupal_get_path('module', 'system') . '/system.js',
'module');
15380
  drupal_add_js(array('dateTime' => array('lookup' =>
url('admin/settings/date-time/lookup'))), 'setting');
1539
1540
  // Date settings:
15410
  $zones = _system_zonelist();
1542
1543
  // Date settings: possible date formats
15440
  $date_short = array('Y-m-d H:i', 'm/d/Y - H:i', 'd/m/Y - H:i', 'Y/m/d -
H:i',
15450
           'd.m.Y - H:i', 'm/d/Y - g:ia', 'd/m/Y - g:ia', 'Y/m/d - g:ia',
15460
           'M j Y - H:i', 'j M Y - H:i', 'Y M j - H:i',
15470
           'M j Y - g:ia', 'j M Y - g:ia', 'Y M j - g:ia');
15480
  $date_medium = array('D, Y-m-d H:i', 'D, m/d/Y - H:i', 'D, d/m/Y - H:i',
15490
          'D, Y/m/d - H:i', 'F j, Y - H:i', 'j F, Y - H:i', 'Y, F j -
H:i',
15500
          'D, m/d/Y - g:ia', 'D, d/m/Y - g:ia', 'D, Y/m/d - g:ia',
15510
          'F j, Y - g:ia', 'j F Y - g:ia', 'Y, F j - g:ia', 'j. F Y -
G:i');
15520
  $date_long = array('l, F j, Y - H:i', 'l, j F, Y - H:i', 'l, Y, F j -
H:i',
15530
        'l, F j, Y - g:ia', 'l, j F Y - g:ia', 'l, Y, F j - g:ia', 'l, j. F
Y - G:i');
1554
1555
  // Date settings: construct choices for user
15560
  foreach ($date_short as $f) {
15570
    $date_short_choices[$f] = format_date(time(), 'custom', $f);
15580
  }
15590
  foreach ($date_medium as $f) {
15600
    $date_medium_choices[$f] = format_date(time(), 'custom', $f);
15610
  }
15620
  foreach ($date_long as $f) {
15630
    $date_long_choices[$f] = format_date(time(), 'custom', $f);
15640
  }
1565
15660
  $date_long_choices['custom'] = $date_medium_choices['custom'] =
$date_short_choices['custom'] = t('Custom format');
1567
15680
  $form['locale'] = array(
15690
    '#type' => 'fieldset',
15700
    '#title' => t('Locale settings'),
1571
  );
1572
15730
  $form['locale']['date_default_timezone'] = array(
15740
    '#type' => 'select',
15750
    '#title' => t('Default time zone'),
15760
    '#default_value' => variable_get('date_default_timezone', 0),
15770
    '#options' => $zones,
15780
    '#description' => t('Select the default site time zone.')
15790
  );
1580
15810
  $form['locale']['configurable_timezones'] = array(
15820
    '#type' => 'radios',
15830
    '#title' => t('User-configurable time zones'),
15840
    '#default_value' => variable_get('configurable_timezones', 1),
15850
    '#options' => array(t('Disabled'), t('Enabled')),
15860
    '#description' => t('When enabled, users can set their own time zone
and dates will be displayed accordingly.')
15870
  );
1588
15890
  $form['locale']['date_first_day'] = array(
15900
    '#type' => 'select',
15910
    '#title' => t('First day of week'),
15920
    '#default_value' => variable_get('date_first_day', 0),
15930
    '#options' => array(0 => t('Sunday'), 1 => t('Monday'), 2 =>
t('Tuesday'), 3 => t('Wednesday'), 4 => t('Thursday'), 5 => t('Friday'), 6
=> t('Saturday')),
15940
    '#description' => t('The first day of the week for calendar views.')
15950
  );
1596
15970
  $form['date_formats'] = array(
15980
    '#type' => 'fieldset',
15990
    '#title' => t('Formatting'),
1600
  );
1601
16020
  $date_format_short = variable_get('date_format_short', $date_short[1]);
16030
  $form['date_formats']['date_format_short'] = array(
16040
    '#prefix' => '<div class="date-container"><div
class="select-container">',
16050
    '#suffix' => '</div>',
16060
    '#type' => 'select',
16070
    '#title' => t('Short date format'),
16080
    '#attributes' => array('class' => 'date-format'),
16090
    '#default_value' => (isset($date_short_choices[$date_format_short]) ?
$date_format_short : 'custom'),
16100
    '#options' => $date_short_choices,
16110
    '#description' => t('The short format of date display.'),
1612
  );
1613
16140
  $default_short_custom = variable_get('date_format_short_custom',
(isset($date_short_choices[$date_format_short]) ? $date_format_short :
''));
16150
  $form['date_formats']['date_format_short_custom'] = array(
16160
    '#prefix' => '<div class="custom-container">',
16170
    '#suffix' => '</div></div>',
16180
    '#type' => 'textfield',
16190
    '#title' => t('Custom short date format'),
16200
    '#attributes' => array('class' => 'custom-format'),
16210
    '#default_value' => $default_short_custom,
16220
    '#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))),
1623
  );
1624
16250
  $date_format_medium = variable_get('date_format_medium',
$date_medium[1]);
16260
  $form['date_formats']['date_format_medium'] = array(
16270
    '#prefix' => '<div class="date-container"><div
class="select-container">',
16280
    '#suffix' => '</div>',
16290
    '#type' => 'select',
16300
    '#title' => t('Medium date format'),
16310
    '#attributes' => array('class' => 'date-format'),
16320
    '#default_value' => (isset($date_medium_choices[$date_format_medium]) ?
$date_format_medium : 'custom'),
16330
    '#options' => $date_medium_choices,
16340
    '#description' => t('The medium sized date display.'),
1635
  );
1636
16370
  $default_medium_custom = variable_get('date_format_medium_custom',
(isset($date_medium_choices[$date_format_medium]) ? $date_format_medium :
''));
16380
  $form['date_formats']['date_format_medium_custom'] = array(
16390
    '#prefix' => '<div class="custom-container">',
16400
    '#suffix' => '</div></div>',
16410
    '#type' => 'textfield',
16420
    '#title' => t('Custom medium date format'),
16430
    '#attributes' => array('class' => 'custom-format'),
16440
    '#default_value' => $default_medium_custom,
16450
    '#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))),
1646
  );
1647
16480
  $date_format_long = variable_get('date_format_long', $date_long[0]);
16490
  $form['date_formats']['date_format_long'] = array(
16500
    '#prefix' => '<div class="date-container"><div
class="select-container">',
16510
    '#suffix' => '</div>',
16520
    '#type' => 'select',
16530
    '#title' => t('Long date format'),
16540
    '#attributes' => array('class' => 'date-format'),
16550
    '#default_value' => (isset($date_long_choices[$date_format_long]) ?
$date_format_long : 'custom'),
16560
    '#options' => $date_long_choices,
16570
    '#description' => t('Longer date format used for detailed display.')
16580
  );
1659
16600
  $default_long_custom = variable_get('date_format_long_custom',
(isset($date_long_choices[$date_format_long]) ? $date_format_long : ''));
16610
  $form['date_formats']['date_format_long_custom'] = array(
16620
    '#prefix' => '<div class="custom-container">',
16630
    '#suffix' => '</div></div>',
16640
    '#type' => 'textfield',
16650
    '#title' => t('Custom long date format'),
16660
    '#attributes' => array('class' => 'custom-format'),
16670
    '#default_value' => $default_long_custom,
16680
    '#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))),
1669
  );
1670
16710
  $form = system_settings_form($form);
1672
  // We will call system_settings_form_submit() manually, so remove it for
now.
16730
  unset($form['#submit']);
16740
  return $form;
16750
}
1676
1677
/**
1678
 * Process system_date_time_settings form submissions.
1679
 */
1680125
function system_date_time_settings_submit($form, &$form_state) {
16810
  if ($form_state['values']['date_format_short'] == 'custom') {
16820
    $form_state['values']['date_format_short'] =
$form_state['values']['date_format_short_custom'];
16830
  }
16840
  if ($form_state['values']['date_format_medium'] == 'custom') {
16850
    $form_state['values']['date_format_medium'] =
$form_state['values']['date_format_medium_custom'];
16860
  }
16870
  if ($form_state['values']['date_format_long'] == 'custom') {
16880
    $form_state['values']['date_format_long'] =
$form_state['values']['date_format_long_custom'];
16890
  }
16900
  return system_settings_form_submit($form, $form_state);
16910
}
1692
1693
/**
1694
 * Return the date for a given format string via Ajax.
1695
 */
1696125
function system_date_time_lookup() {
16970
  $result = format_date(time(), 'custom', $_GET['format']);
16980
  echo drupal_to_js($result);
16990
  exit;
17000
}
1701
1702
/**
1703
 * Form builder; Configure the site's maintenance status.
1704
 *
1705
 * @ingroup forms
1706
 * @see system_settings_form()
1707
 */
1708125
function system_site_maintenance_settings() {
1709
17100
  $form['site_offline'] = array(
17110
    '#type' => 'radios',
17120
    '#title' => t('Site status'),
17130
    '#default_value' => variable_get('site_offline', 0),
17140
    '#options' => array(t('Online'), t('Offline')),
17150
    '#description' => t('When set to "Online", all visitors will be able to
browse your site normally. When set to "Offline", 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 offline
message configured below. Authorized users can log in during "Offline" mode
directly via the <a href="@user-login">user login</a> page.',
array('@user-login' => url('user'))),
1716
  );
1717
17180
  $form['site_offline_message'] = array(
17190
    '#type' => 'textarea',
17200
    '#title' => t('Site offline message'),
17210
    '#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')))),
17220
    '#description' => t('Message to show visitors when the site is in
offline mode.')
17230
  );
1724
17250
  return system_settings_form($form);
17260
}
1727
1728
/**
1729
 * Form builder; Configure Clean URL settings.
1730
 *
1731
 * @ingroup forms
1732
 * @see system_settings_form()
1733
 */
1734125
function system_clean_url_settings() {
17350
  $form['clean_url'] = array(
17360
    '#type' => 'radios',
17370
    '#title' => t('Clean URLs'),
17380
    '#default_value' => variable_get('clean_url', 0),
17390
    '#options' => array(t('Disabled'), t('Enabled')),
17400
    '#description' => t('This option makes Drupal emit "clean" URLs (i.e.
without <code>?q=</code> in the URL).'),
1741
  );
1742
17430
  if (!variable_get('clean_url', 0)) {
17440
    if (strpos(request_uri(), '?q=') !== FALSE) {
17450
      drupal_add_js(drupal_get_path('module', 'system') . '/system.js',
'module');
1746
17470
      $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>';
1748
17490
      $form['clean_url']['#disabled'] = TRUE;
17500
      $form['clean_url']['#prefix'] = '<div id="clean-url">';
17510
      $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>';
17520
    }
1753
    else {
17540
      $form['clean_url']['#description'] .= ' <div class="ok">' . t('Your
server has been successfully tested to support this feature.') . '</div>';
1755
    }
17560
  }
1757
17580
  return system_settings_form($form);
17590
}
1760
1761
/**
1762
 * Menu callback: displays the site status report. Can also be used as a
pure check.
1763
 *
1764
 * @param $check
1765
 *   If true, only returns a boolean whether there are system status
errors.
1766
 */
1767125
function system_status($check = FALSE) {
1768
  // Load .install files
17693
  include_once './includes/install.inc';
17703
  drupal_load_updates();
1771
1772
  // Check run-time requirements and status information.
17733
  $requirements = module_invoke_all('requirements', 'runtime');
17743
  usort($requirements, '_system_sort_requirements');
1775
17763
  if ($check) {
17772
    return drupal_requirements_severity($requirements) ==
REQUIREMENT_ERROR;
17780
  }
1779
  // MySQL import might have set the uid of the anonymous user to
autoincrement
1780
  // value. Let's try fixing it. See http://drupal.org/node/204411
17811
  db_query("UPDATE {users} SET uid = uid - uid WHERE name = '' AND pass =
'' AND status = 0");
1782
17831
  return theme('status_report', $requirements);
17840
}
1785
1786
/**
1787
 * Menu callback: run cron manually.
1788
 */
1789125
function system_run_cron() {
1790
  // Run cron manually
17911
  if (drupal_cron_run()) {
17921
    drupal_set_message(t('Cron ran successfully.'));
17931
  }
1794
  else {
17950
    drupal_set_message(t('Cron run failed.'), 'error');
1796
  }
1797
17981
  drupal_goto('admin/reports/status');
17990
}
1800
1801
/**
1802
 * Menu callback: return information about PHP.
1803
 */
1804125
function system_php() {
18050
  phpinfo(INFO_ALL);
18060
  exit();
18070
}
1808
1809
/**
1810
 * Theme a SQL result table.
1811
 *
1812
 * @param $data
1813
 *   The actual table data.
1814
 * @param $keys
1815
 *   Data keys and descriptions.
1816
 * @return
1817
 *   The output HTML.
1818
 */
1819125
function _system_sql($data, $keys) {
18200
  $rows = array();
18210
  foreach ($keys as $key => $explanation) {
18220
    if (isset($data[$key])) {
18230
      $rows[] = array(check_plain($key), check_plain($data[$key]),
$explanation);
18240
    }
18250
  }
1826
18270
  return theme('table', array(t('Variable'), t('Value'), t('Description')),
$rows);
18280
}
1829
1830
/**
1831
 * Menu callback: return information about the database.
1832
 */
1833125
function system_sql() {
1834
18350
  $result = db_query("SHOW STATUS");
18360
  while ($entry = db_fetch_object($result)) {
1837
    // 'SHOW STATUS' returns fields named 'Variable_name' and 'Value',
1838
    // case is important.
18390
    $data[$entry->Variable_name] = $entry->Value;
18400
  }
1841
18420
  $output  = '<h2>' . t('Command counters') . '</h2>';
18430
  $output .= _system_sql($data, array(
18440
   'Com_select' => t('The number of !sql statements.', array('!sql' =>
'<code>SELECT</code>')),
18450
   'Com_insert' => t('The number of !sql statements.', array('!sql' =>
'<code>INSERT</code>')),
18460
   'Com_update' => t('The number of !sql statements.', array('!sql' =>
'<code>UPDATE</code>')),
18470
   'Com_delete' => t('The number of !sql statements.', array('!sql' =>
'<code>DELETE</code>')),
18480
   'Com_lock_tables' => t('The number of table locks.'),
18490
   'Com_unlock_tables' => t('The number of table unlocks.')
18500
  ));
1851
18520
  $output .= '<h2>' . t('Query performance') . '</h2>';
18530
  $output .= _system_sql($data, array(
18540
   'Select_full_join' => t('The number of joins without an index; should be
zero.'),
18550
   'Select_range_check' => t('The number of joins without keys that check
for key usage after each row; should be zero.'),
18560
   'Sort_scan' => t('The number of sorts done without using an index;
should be zero.'),
18570
   'Table_locks_immediate' => t('The number of times a lock could be
acquired immediately.'),
18580
   'Table_locks_waited' => t('The number of times the server had to wait
for a lock.')
18590
  ));
1860
18610
  $output .= '<h2>' . t('Query cache information') . '</h2>';
18620
  $output .= '<p>' . t('The MySQL query cache can improve performance of
your site by storing the result of queries. Then, if an identical query is
received later, the MySQL server retrieves the result from the query cache
rather than parsing and executing the statement again.') . '</p>';
18630
  $output .= _system_sql($data, array(
18640
   'Qcache_queries_in_cache' => t('The number of queries in the query
cache.'),
18650
   'Qcache_hits' => t('The number of times MySQL found previous results in
the cache.'),
18660
   'Qcache_inserts' => t('The number of times MySQL added a query to the
cache (misses).'),
18670
   'Qcache_lowmem_prunes' => t('The number of times MySQL had to remove
queries from the cache because it ran out of memory. Ideally should be
zero.')
18680
  ));
1869
18700
  return $output;
18710
}
1872
1873
/**
1874
 * Default page callback for batches.
1875
 */
1876125
function system_batch_page() {
18770
  require_once './includes/batch.inc';
18780
  $output = _batch_page();
18790
  if ($output === FALSE) {
18800
    drupal_access_denied();
18810
  }
18820
  elseif (isset($output)) {
1883
    // Force a page without blocks or messages to
1884
    // display a list of collected messages later.
18850
    print theme('page', $output, FALSE, FALSE);
18860
  }
18870
}
1888
1889
/**
1890
 * This function formats an administrative block for display.
1891
 *
1892
 * @param $block
1893
 *   An array containing information about the block. It should
1894
 *   include a 'title', a 'description' and a formatted 'content'.
1895
 * @ingroup themeable
1896
 */
1897125
function theme_admin_block($block) {
1898
  // Don't display the block if it has no content to display.
18993
  if (empty($block['content'])) {
19001
    return '';
19010
  }
1902
1903
  $output = <<< EOT
19042
  <div class="admin-panel">
1905
    <h3>
19062
      $block[title]
1907
    </h3>
1908
    <div class="body">
1909
      <p class="description">
19102
        $block[description]
1911
      </p>
19122
      $block[content]
1913
    </div>
19142
  </div>
19152
EOT;
19162
  return $output;
19170
}
1918
1919
/**
1920
 * This function formats the content of an administrative block.
1921
 *
1922
 * @param $block
1923
 *   An array containing information about the block. It should
1924
 *   include a 'title', a 'description' and a formatted 'content'.
1925
 * @ingroup themeable
1926
 */
1927125
function theme_admin_block_content($content) {
19284
  if (!$content) {
19291
    return '';
19300
  }
1931
19323
  if (system_admin_compact_mode()) {
19330
    $output = '<ul class="menu">';
19340
    foreach ($content as $item) {
19350
      $output .= '<li class="leaf">' . l($item['title'], $item['href'],
$item['localized_options']) . '</li>';
19360
    }
19370
    $output .= '</ul>';
19380
  }
1939
  else {
19403
    $output = '<dl class="admin-list">';
19413
    foreach ($content as $item) {
19423
      $output .= '<dt>' . l($item['title'], $item['href'],
$item['localized_options']) . '</dt>';
19433
      $output .= '<dd>' . $item['description'] . '</dd>';
19443
    }
19453
    $output .= '</dl>';
1946
  }
19473
  return $output;
19480
}
1949
1950
/**
1951
 * This function formats an administrative page for viewing.
1952
 *
1953
 * @param $blocks
1954
 *   An array of blocks to display. Each array should include a
1955
 *   'title', a 'description', a formatted 'content' and a
1956
 *   'position' which will control which container it will be
1957
 *   in. This is usually 'left' or 'right'.
1958
 * @ingroup themeable
1959
 */
1960125
function theme_admin_page($blocks) {
19612
  $stripe = 0;
19622
  $container = array();
1963
19642
  foreach ($blocks as $block) {
19652
    if ($block_output = theme('admin_block', $block)) {
19661
      if (empty($block['position'])) {
1967
        // perform automatic striping.
19680
        $block['position'] = ++$stripe % 2 ? 'left' : 'right';
19690
      }
19701
      if (!isset($container[$block['position']])) {
19711
        $container[$block['position']] = '';
19721
      }
19731
      $container[$block['position']] .= $block_output;
19741
    }
19752
  }
1976
19772
  $output = '<div class="admin clear-block">';
19782
  $output .= theme('system_compact_link');
1979
19802
  foreach ($container as $id => $data) {
19811
    $output .= '<div class="' . $id . ' clear-block">';
19821
    $output .= $data;
19831
    $output .= '</div>';
19841
  }
19852
  $output .= '</div>';
19862
  return $output;
19870
}
1988
1989
/**
1990
 * Theme output of the dashboard page.
1991
 *
1992
 * @param $menu_items
1993
 *   An array of modules to be displayed.
1994
 * @ingroup themeable
1995
 */
1996125
function theme_system_admin_by_module($menu_items) {
19972
  $stripe = 0;
19982
  $output = '';
19992
  $container = array('left' => '', 'right' => '');
20002
  $flip = array('left' => 'right', 'right' => 'left');
20012
  $position = 'left';
2002
2003
  // Iterate over all modules
20042
  foreach ($menu_items as $module => $block) {
20051
    list($description, $items) = $block;
2006
2007
    // Output links
20081
    if (count($items)) {
20091
      $block = array();
20101
      $block['title'] = $module;
20111
      $block['content'] = theme('item_list', $items);
20121
      $block['description'] = t($description);
2013
20141
      if ($block_output = theme('admin_block', $block)) {
20151
        if (!isset($block['position'])) {
2016
          // Perform automatic striping.
20171
          $block['position'] = $position;
20181
          $position = $flip[$position];
20191
        }
20201
        $container[$block['position']] .= $block_output;
20211
      }
20221
    }
20231
  }
2024
20252
  $output = '<div class="admin clear-block">';
20262
  foreach ($container as $id => $data) {
20272
    $output .= '<div class="' . $id . ' clear-block">';
20282
    $output .= $data;
20292
    $output .= '</div>';
20302
  }
20312
  $output .= '</div>';
2032
20332
  return $output;
20340
}
2035
2036
/**
2037
 * Theme requirements status report.
2038
 *
2039
 * @param $requirements
2040
 *   An array of requirements.
2041
 * @ingroup themeable
2042
 */
2043125
function theme_status_report(&$requirements) {
20441
  $i = 0;
20451
  $output = '<table class="system-status-report">';
20461
  foreach ($requirements as $requirement) {
20471
    if (empty($requirement['#type'])) {
20481
      $class = ++$i % 2 == 0 ? 'even' : 'odd';
2049
2050
      $classes = array(
20511
        REQUIREMENT_INFO => 'info',
20521
        REQUIREMENT_OK => 'ok',
20531
        REQUIREMENT_WARNING => 'warning',
20541
        REQUIREMENT_ERROR => 'error',
20551
      );
20561
      $class = $classes[isset($requirement['severity']) ?
(int)$requirement['severity'] : 0] . ' ' . $class;
2057
2058
      // Output table row(s)
20591
      if (!empty($requirement['description'])) {
20601
        $output .= '<tr class="' . $class . ' merge-down"><th>' .
$requirement['title'] . '</th><td>' . $requirement['value'] .
'</td></tr>';
20611
        $output .= '<tr class="' . $class . ' merge-up"><td colspan="2">' .
$requirement['description'] . '</td></tr>';
20621
      }
2063
      else {
20641
        $output .= '<tr class="' . $class . '"><th>' .
$requirement['title'] . '</th><td>' . $requirement['value'] .
'</td></tr>';
2065
      }
20661
    }
20671
  }
2068
20691
  $output .= '</table>';
20701
  return $output;
20710
}
2072
2073
/**
2074
 * Theme callback for the modules form.
2075
 *
2076
 * @param $form
2077
 *   An associative array containing the structure of the form.
2078
 * @ingroup themeable
2079
 */
2080125
function theme_system_modules_fieldset($form) {
2081
  // Individual table headers.
20826
  $rows = array();
2083
  // Iterate through all the modules, which are
2084
  // children of this fieldset.
20856
  foreach (element_children($form) as $key) {
2086
    // Stick it into $module for easier accessing.
20876
    $module = $form[$key];
20886
    $row = array();
20896
    unset($module['enable']['#title']);
20906
    $row[] = array('class' => 'checkbox', 'data' =>
drupal_render($module['enable']));
20916
    $row[] = '<strong>'. drupal_render($module['name']) .'</strong>';
20926
    $row[] = drupal_render($module['version']);
20936
    $description = '';
2094
    // If we have help, it becomes the first part
2095
    // of the description - with CSS, it is float: right'd.
20966
    if (isset($module['help'])) {
20976
      $description = '<div class="module-help">'.
drupal_render($module['help']) .'</div>';
20986
    }
2099
    // Add the description, along with any dependencies.
21006
    $description .= drupal_render($module['description']);
21016
    if ($module['#dependencies']) {
21026
     $description .= '<div class="admin-dependencies">' . t('Depends on: ')
. implode(', ', $module['#dependencies']) . '</div>';
21036
    }
21046
    if ($module['#dependents']) {
21056
     $description .= '<div class="admin-dependencies">' . t('Required by:
') . implode(', ', $module['#dependents']) . '</div>';
21066
    }
21076
    $row[] = array('data' => $description, 'class' => 'description');
21086
    $rows[] = $row;
21096
  }
2110
21116
  return theme('table', $form['#header'], $rows);
21120
}
2113
2114
/**
2115
 * Themes an incompatible message.
2116
 *
2117
 * @ingroup themeable
2118
 * @param $message
2119
 *   The form array representing the currently disabled modules.
2120
 * @return
2121
 *   An HTML string for the message.
2122
 */
2123125
function theme_system_modules_incompatible($message) {
21240
  return '<div class="incompatible">'. $message .'</div>';
21250
}
2126
2127
/**
2128
 * Themes a table of currently disabled modules.
2129
 *
2130
 * @ingroup themeable
2131
 * @param $form
2132
 *   The form array representing the currently disabled modules.
2133
 * @return
2134
 *   An HTML string representing the table.
2135
 */
2136125
function theme_system_modules_uninstall($form) {
2137
  // No theming for the confirm form.
21382
  if (isset($form['confirm'])) {
21390
    return drupal_render($form);
21400
  }
2141
2142
  // Table headers.
21432
  $header = array(t('Uninstall'),
21442
    t('Name'),
21452
    t('Description'),
21462
  );
2147
2148
  // Display table.
21492
  $rows = array();
21502
  foreach (element_children($form['modules']) as $module) {
21511
    $rows[] = array(
21521
      array('data' => drupal_render($form['uninstall'][$module]), 'align'
=> 'center'),
21531
      '<strong><label for="' . $form['uninstall'][$module]['#id'] . '">' .
drupal_render($form['modules'][$module]['name']) . '</label></strong>',
21541
      array('data' =>
drupal_render($form['modules'][$module]['description']), 'class' =>
'description'),
2155
    );
21561
  }
2157
2158
  // Only display table if there are modules that can be uninstalled.
21592
  if (empty($rows)) {
21601
    $rows[] = array(array('data' => t('No modules are available to
uninstall.'), 'colspan' => '3', 'align' => 'center', 'class' =>
'message'));
21611
  }
2162
21632
  $output  = theme('table', $header, $rows);
21642
  $output .= drupal_render($form);
2165
21662
  return $output;
21670
}
2168
2169
/**
2170
 * Theme the theme select form.
2171
 * @param $form
2172
 *   An associative array containing the structure of the form.
2173
 * @ingroup themeable
2174
 */
2175125
function theme_system_theme_select_form($form) {
21760
  foreach (element_children($form) as $key) {
21770
    $row = array();
21780
    if (isset($form[$key]['description']) &&
is_array($form[$key]['description'])) {
21790
      $row[] = drupal_render($form[$key]['screenshot']);
21800
      $row[] = drupal_render($form[$key]['description']);
21810
      $row[] = drupal_render($form['theme'][$key]);
21820
    }
21830
    $rows[] = $row;
21840
  }
2185
21860
  $header = array(t('Screenshot'), t('Name'), t('Selected'));
21870
  $output = theme('table', $header, $rows);
21880
  return $output;
21890
}
2190
2191
/**
2192
 * Theme function for the system themes form.
2193
 *
2194
 * @param $form
2195
 *   An associative array containing the structure of the form.
2196
 * @ingroup themeable
2197
 */
2198125
function theme_system_themes_form($form) {
21990
  foreach (element_children($form) as $key) {
2200
    // Only look for themes
22010
    if (!isset($form[$key]['info'])) {
22020
      continue;
22030
    }
2204
2205
    // Fetch info
22060
    $info = $form[$key]['info']['#value'];
2207
    // Localize theme description.
22080
    $description = t($info['description']);
2209
    // Make sure it is compatible and render the checkbox if so.
22100
    if (isset($form['status']['#incompatible_themes_core'][$key])) {
22110
      unset($form['status'][$key]);
22120
      $status = theme('image', 'misc/watchdog-error.png',
t('incompatible'), t('Incompatible with this version of Drupal core'));
22130
      $description .= '<div class="incompatible">' . t('This version is
incompatible with the !core_version version of Drupal core.',
array('!core_version' => VERSION)) . '</div>';
22140
    }
22150
    elseif (isset($form['status']['#incompatible_themes_php'][$key])) {
22160
      unset($form['status'][$key]);
22170
      $status = theme('image', 'misc/watchdog-error.png',
t('incompatible'), t('Incompatible with this version of PHP'));
22180
      $php_required = $form['status']['#incompatible_themes_php'][$key];
22190
      if (substr_count($php_required, '.') < 2) {
22200
        $php_required .= '.*';
22210
      }
22220
      $description .= '<div class="incompatible">' . t('This theme requires
PHP version @php_required and is incompatible with PHP version
!php_version.', array('@php_required' => $php_required, '!php_version' =>
phpversion())) . '</div>';
22230
    }
2224
    else {
22250
      $status = drupal_render($form['status'][$key]);
2226
    }
2227
2228
    // Style theme info
22290
    $theme = '<div class="theme-info"><h2>' . $info['name'] . '</h2><div
class="description">' . $description . '</div></div>';
2230
2231
    // Build rows
22320
    $row = array();
22330
    $row[] = drupal_render($form[$key]['screenshot']);
22340
    $row[] = $theme;
22350
    $row[] = isset($info['version']) ? $info['version'] : '';
22360
    $row[] = array('data' => $status, 'align' => 'center');
22370
    if ($form['theme_default']) {
22380
      $row[] = array('data' => drupal_render($form['theme_default'][$key]),
'align' => 'center');
22390
      $row[] = array('data' => drupal_render($form[$key]['operations']),
'align' => 'center');
22400
    }
22410
    $rows[] = $row;
22420
  }
2243
22440
  $header = array(t('Screenshot'), t('Name'), t('Version'), t('Enabled'),
t('Default'), t('Operations'));
22450
  $output = theme('table', $header, $rows);
22460
  $output .= drupal_render($form);
22470
  return $output;
2248125
}