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

Line #Times calledCode
1
<?php
2
// $Id: system.admin.inc,v 1.102 2008/10/16 20:23:08 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
 */
1299
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
 */
7399
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
 */
8799
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
 */
12199
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
 */
14199
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
 */
17699
function system_themes_form() {
177
1783
  drupal_clear_css_cache();
1793
  $themes = system_theme_data();
180
1813
  uasort($themes, 'system_sort_modules_by_info_name');
182
1833
  $status = array();
1843
  $incompatible_core = array();
1853
  $incompatible_php = array();
186
1873
  foreach ($themes as $theme) {
1883
    $screenshot = NULL;
1893
    $theme_key = $theme->name;
1903
    while ($theme_key) {
1913
      if (file_exists($themes[$theme_key]->info['screenshot'])) {
1923
        $screenshot = $themes[$theme_key]->info['screenshot'];
1933
        break;
1940
      }
1950
      $theme_key = isset($themes[$theme_key]->info['base theme']) ?
$themes[$theme_key]->info['base theme'] : NULL;
1960
    }
1973
    $screenshot = $screenshot ? theme('image', $screenshot, t('Screenshot
for %theme theme', array('%theme' => $theme->info['name'])), '',
array('class' => 'screenshot'), FALSE) : t('no screenshot');
198
1993
    $form[$theme->name]['screenshot'] = array('#markup' => $screenshot);
2003
    $form[$theme->name]['info'] = array(
2013
      '#type' => 'value',
2023
      '#value' => $theme->info,
203
    );
2043
    $options[$theme->name] = '';
205
2063
    if (!empty($theme->status) || $theme->name ==
variable_get('admin_theme', '0')) {
2073
      $form[$theme->name]['operations'] = array('#markup' =>
l(t('configure'), 'admin/build/themes/settings/' . $theme->name) );
2083
    }
209
    else {
210
      // Dummy element for drupal_render. Cleaner than adding a check in
the theme function.
2113
      $form[$theme->name]['operations'] = array();
212
    }
2133
    if (!empty($theme->status)) {
2143
      $status[] = $theme->name;
2153
    }
216
    else {
217
      // Ensure this theme is compatible with this version of core.
2183
      if (!isset($theme->info['core']) || $theme->info['core'] !=
DRUPAL_CORE_COMPATIBILITY) {
2190
        $incompatible_core[] = $theme->name;
2200
      }
2213
      if (version_compare(phpversion(), $theme->info['php']) < 0) {
2220
        $incompatible_php[$theme->name] = $theme->info['php'];
2230
      }
224
    }
2253
  }
226
2273
  $form['status'] = array(
2283
    '#type' => 'checkboxes',
2293
    '#options' => $options,
2303
    '#default_value' => $status,
2313
    '#incompatible_themes_core' => drupal_map_assoc($incompatible_core),
2323
    '#incompatible_themes_php' => $incompatible_php,
233
  );
2343
  $form['theme_default'] = array(
2353
    '#type' => 'radios',
2363
    '#options' => $options,
2373
    '#default_value' => variable_get('theme_default', 'garland'),
238
  );
2393
  $form['buttons']['submit'] = array(
2403
    '#type' => 'submit',
2413
    '#value' => t('Save configuration'),
242
  );
2433
  $form['buttons']['reset'] = array(
2443
    '#type' => 'submit',
2453
    '#value' => t('Reset to defaults'),
246
  );
2473
  return $form;
2480
}
249
250
/**
251
 * Process system_themes_form form submissions.
252
 */
25399
function system_themes_form_submit($form, &$form_state) {
254
255
  // Store list of previously enabled themes and disable all themes
2561
  $old_theme_list = $new_theme_list = array();
2571
  foreach (list_themes() as $theme) {
2581
    if ($theme->status) {
2591
      $old_theme_list[] = $theme->name;
2601
    }
2611
  }
2621
  db_query("UPDATE {system} SET status = 0 WHERE type = 'theme'");
263
2641
  if ($form_state['values']['op'] == t('Save configuration')) {
2651
    if (is_array($form_state['values']['status'])) {
2661
      foreach ($form_state['values']['status'] as $key => $choice) {
267
        // Always enable the default theme, despite its status checkbox
being checked:
2681
        if ($choice || $form_state['values']['theme_default'] == $key) {
2691
          system_initialize_theme_blocks($key);
2701
          $new_theme_list[] = $key;
2711
          db_query("UPDATE {system} SET status = 1 WHERE type = 'theme' and
name = '%s'", $key);
2721
        }
2731
      }
2741
    }
2751
    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
    }
2821
    variable_set('theme_default', $form_state['values']['theme_default']);
2831
  }
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
2911
  list_themes(TRUE);
2921
  menu_rebuild();
2931
  drupal_theme_rebuild();
2941
  drupal_set_message(t('The configuration options have been saved.'));
2951
  $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.
3001
  module_invoke('locale', 'system_update', array_diff($new_theme_list,
$old_theme_list));
301
3021
  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
 */
31599
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 ($filepath = file_unmanaged_copy($file->filepath, $filename,
FILE_EXISTS_REPLACE)) {
3420
      $_POST['default_logo'] = 0;
3430
      $_POST['logo_path'] = $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 ($filepath = file_unmanaged_copy($file->filepath, $filename,
FILE_EXISTS_REPLACE)) {
3570
      $_POST['default_favicon'] = 0;
3580
      $_POST['favicon_path'] = $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 = DRUPAL_ROOT . '/' . 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 = DRUPAL_ROOT . '/' . 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
 */
53199
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
 */
56199
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
 */
60799
function system_modules($form_state = array()) {
608
  // Clear all caches.
60911
  registry_rebuild();
61011
  drupal_theme_rebuild();
61111
  node_types_rebuild();
61211
  menu_rebuild();
61311
  cache_clear_all('schema', 'cache');
614
  // Get current list of modules.
61511
  $files = module_rebuild_cache();
616
617
  // Remove hidden modules from display list.
61811
  foreach ($files as $filename => $file) {
61911
    if (!empty($file->info['hidden']) || !empty($file->info['required'])) {
62011
      unset($files[$filename]);
62111
    }
62211
  }
623
62411
  uasort($files, 'system_sort_modules_by_info_name');
625
62611
  if (!empty($form_state['storage'])) {
627
    // If the modules form was submitted, then first system_modules_submit
runs
628
    // and if there are unfilled dependencies, then form_state['storage']
is
629
    // filled, triggering a rebuild. In this case we need to show a confirm
630
    // form.
6311
    return system_modules_confirm_form($files, $form_state['storage']);
6320
  }
63311
  $dependencies = array();
63411
  $modules = array();
63511
  $form['modules'] = array('#tree' => TRUE);
636
637
  // Used when checking if module implements a help page.
63811
  $help_arg = module_exists('help') ? drupal_help_arg() : FALSE;
639
640
  // Iterate through each of the modules.
64111
  foreach ($files as $filename => $module) {
64211
    $extra = array();
64311
    $extra['enabled'] = (bool) $module->status;
644
    // If this module has dependencies, add them to the array.
64511
    if (is_array($module->info['dependencies'])) {
64611
      foreach ($module->info['dependencies'] as $dependency) {
64711
        if (!isset($files[$dependency])) {
6480
          $extra['dependencies'][$dependency] = t('@module (<span
class="admin-missing">missing</span>)', array('@module' =>
drupal_ucfirst($dependency)));
6490
          $extra['disabled'] = TRUE;
6500
        }
65111
        elseif (!$files[$dependency]->status) {
65211
          $extra['dependencies'][$dependency] = t('@module (<span
class="admin-disabled">disabled</span>)', array('@module' =>
$files[$dependency]->info['name']));
65311
        }
654
        else {
65511
          $extra['dependencies'][$dependency] = t('@module (<span
class="admin-enabled">enabled</span>)', array('@module' =>
$files[$dependency]->info['name']));
656
        }
65711
      }
65811
    }
659
    // Generate link for module's help page, if there is one.
66011
    if ($help_arg && module_hook($filename, 'help')) {
66111
      if (module_invoke($filename, 'help', "admin/help#$filename",
$help_arg)) {
662
        // Module has a help page.
66311
        $extra['help'] = theme('more_help_link',
url("admin/help/$filename"));
66411
      }
66511
    }
666
    // Mark dependents disabled so user can not remove modules being
depended on.
66711
    $dependents = array();
66811
    foreach ($module->info['dependents'] as $dependent) {
66911
      if ($files[$dependent]->status == 1) {
6701
        $extra['dependents'][] = t('@module (<span
class="admin-enabled">enabled</span>)', array('@module' =>
$files[$dependent]->info['name']));
6711
        $extra['disabled'] = TRUE;
6721
      }
673
      else {
67411
        $extra['dependents'][] = t('@module (<span
class="admin-disabled">disabled</span>)', array('@module' =>
$files[$dependent]->info['name']));
675
      }
67611
    }
67711
    $form['modules'][$module->info['package']][$filename] =
_system_modules_build_row($module->info, $extra);
67811
  }
679
  // Add basic information to the fieldsets.
68011
  foreach (element_children($form['modules']) as $package) {
6810
    $form['modules'][$package] += array(
68211
      '#type' => 'fieldset',
68311
      '#title' => t($package),
68411
      '#collapsible' => TRUE,
68511
      '#theme' => 'system_modules_fieldset',
686
      '#header' => array(
68711
        array('data' => t('Enabled'), 'class' => 'checkbox'),
68811
        t('Name'),
68911
        t('Version'),
69011
        t('Description'),
69111
      ),
692
    );
69311
  }
694
69511
  $form['submit'] = array(
69611
    '#type' => 'submit',
69711
    '#value' => t('Save configuration'),
698
  );
69911
  $form['#action'] = url('admin/build/modules/list/confirm');
700
70111
  return $form;
7020
}
703
704
/**
705
 * Array sorting callback; sorts modules or themes by their name.
706
 */
70799
function system_sort_modules_by_info_name($a, $b) {
70814
  return strcasecmp($a->info['name'], $b->info['name']);
7090
}
710
711
/**
712
 * Build a table row for the system modules page.
713
 */
71499
function _system_modules_build_row($info, $extra) {
715
  // Add in the defaults.
716
  $extra += array(
71711
    'dependencies' => array(),
71811
    'dependents' => array(),
71911
    'disabled' => FALSE,
72011
    'enabled' => FALSE,
72111
    'help' => '',
7220
  );
723
  $form = array(
72411
    '#tree' => TRUE,
72511
  );
726
  // Set the basic properties.
72711
  $form['name'] = array(
72811
    '#markup' => t($info['name']),
729
  );
73011
  $form['description'] = array(
73111
    '#markup' => t($info['description']),
732
  );
73311
  $form['version'] = array(
73411
    '#markup' => $info['version'],
735
  );
73611
  $form['#dependencies'] = $extra['dependencies'];
73711
  $form['#dependents'] = $extra['dependents'];
738
739
  // Check the compatibilities.
74011
  $compatible = TRUE;
74111
  $status_short = '';
74211
  $status_long = '';
743
744
  // Check the core compatibility.
74511
  if (!isset($info['core']) || $info['core'] != DRUPAL_CORE_COMPATIBILITY
|| empty($info['files'])) {
7460
    $compatible = FALSE;
7470
    $status_short .= t('Incompatible with this version of Drupal core. ');
7480
    $status_long .= t('This version is incompatible with the !core_version
version of Drupal core. ', array('!core_version' => VERSION));
7490
  }
750
751
  // Ensure this module is compatible with the currently installed version
of PHP.
75211
  if (version_compare(phpversion(), $info['php']) < 0) {
7530
    $compatible = FALSE;
7540
    $status_short .= t('Incompatible with this version of PHP');
7550
    if (substr_count($info['php'], '.') < 2) {
7560
      $php_required .= '.*';
7570
    }
7580
    $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()));
7590
  }
760
761
  // If this module is compatible, present a checkbox indicating
762
  // this module may be installed. Otherwise, show a big red X.
76311
  if ($compatible) {
76411
    $form['enable'] = array(
76511
      '#type' => 'checkbox',
76611
      '#title' => t('Enable'),
76711
      '#default_value' => $extra['enabled'],
768
    );
76911
    if ($extra['disabled']) {
7701
      $form['enable']['#disabled'] = TRUE;
7711
    }
77211
  }
773
  else {
7740
    $form['enable'] = array(
7750
      '#markup' =>  theme('image', 'misc/watchdog-error.png',
t('incompatible'), $status_short),
776
    );
7770
    $form['description']['#markup'] .= theme('system_modules_incompatible',
$status_long);
778
  }
779
780
  // Show a "more help" link for modules that have them.
78111
  if ($extra['help']) {
78211
    $form['help'] = array(
78311
      '#markup' => $extra['help'],
784
    );
78511
  }
78611
  return $form;
7870
}
788
789
/**
790
 * Display confirmation form for dependencies.
791
 *
792
 * @param $modules
793
 *   Array of module file objects as returned from module_rebuild_cache().
794
 * @param $storage
795
 *   The contents of $form_state['storage']; an array with two
796
 *   elements: the list of dependencies and the list of status
797
 *   form field values from the previous screen.
798
 * @ingroup forms
799
 */
80099
function system_modules_confirm_form($modules, $storage) {
8011
  $form = array();
8021
  $items = array();
803
8041
  $form['validation_modules'] = array('#type' => 'value', '#value' =>
$modules);
8051
  $form['status']['#tree'] = TRUE;
806
8071
  foreach ($storage['dependencies'] as $info) {
808
    $t_argument = array(
8091
      '@module' => $info['name'],
8101
      '@dependencies' => implode(', ', $info['dependencies']),
8111
    );
8121
    $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);
8131
  }
8141
  $form['text'] = array('#markup' => theme('item_list', $items));
815
8161
  if ($form) {
817
    // Set some default form values
8181
    $form = confirm_form(
8191
      $form,
8201
      t('Some required modules must be enabled'),
8211
      'admin/build/modules',
8221
      t('Would you like to continue with enabling the above?'),
8231
      t('Continue'),
8241
      t('Cancel'));
8251
    return $form;
8260
  }
8270
}
828
829
/**
830
 * Submit callback; handles modules form submission.
831
 */
83299
function system_modules_submit($form, &$form_state) {
8334
  include_once DRUPAL_ROOT . '/includes/install.inc';
8344
  $modules = array();
835
  // If we're not coming from the confirmation form, build the list of
modules.
8364
  if (!isset($form_state['storage'])) {
8373
    foreach ($form_state['values']['modules'] as $group_name => $group) {
8383
      foreach ($group as $module => $enabled) {
8393
        $modules[$module] = array('group' => $group_name, 'enabled' =>
$enabled['enable']);
8403
      }
8413
    }
8423
  }
843
  else {
844
    // If we are coming from the confirmation form, fetch
845
    // the modules out of $form_state.
8461
    $modules = $form_state['storage']['modules'];
847
  }
848
849
  // Get a list of all modules, for building dependencies with.
8504
  $files = module_rebuild_cache();
851
852
  // The modules to be enabled.
8534
  $modules_to_be_enabled = array();
854
  // The modules to be disabled.
8554
  $disable_modules = array();
856
  // The modules to be installed.
8574
  $new_modules = array();
858
  // The un-met dependencies.
8594
  $dependencies = array();
860
  // Go through each module, finding out
861
  // if we should enable, install, or disable it,
862
  // and if it has any un-met dependencies.
8634
  foreach ($modules as $name => $module) {
864
    // If it's enabled, find out whether to just
865
    // enable it, or install it.
8664
    if ($module['enabled']) {
8674
      if (drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED)
{
8683
        $new_modules[$name] = $name;
8693
      }
8704
      elseif (!module_exists($name)) {
8710
        $modules_to_be_enabled[$name] = $name;
8720
      }
873
      // If we're not coming from a confirmation form,
874
      // search dependencies. Otherwise, the user will have already
875
      // approved of the depdent modules being enabled.
8764
      if (empty($form_state['storage'])) {
8773
        foreach ($form['modules'][$module['group']][$name]['#dependencies']
as $dependency => $string) {
8781
          if (!$modules[$dependency]['enabled']) {
8791
            if (!isset($dependencies[$name])) {
8801
              $dependencies[$name]['name'] = $files[$name]->info['name'];
8811
            }
8821
            $dependencies[$name]['dependencies'][$dependency] =
$files[$dependency]->info['name'];
8831
          }
8841
          $modules[$dependency] = array('group' =>
$files[$dependency]->info['package'], 'enabled' => TRUE);
8851
        }
8863
      }
8874
    }
8884
  }
889
  // A second loop is necessary, otherwise the modules set to be enabled in
the
890
  // previous loop would not be found.
8914
  foreach ($modules as $name => $module) {
8924
    if (module_exists($name) && !$module['enabled']) {
8931
      $disable_modules[$name] = $name;
8941
    }
8954
  }
8964
  if ($dependencies) {
897
    // If there where un-met dependencies and they haven't confirmed don't
process
898
    // the submission yet. Store the form submission data needed later.
8991
    if (!isset($form_state['values']['confirm'])) {
9001
      $form_state['storage'] = array('dependencies' => $dependencies,
'modules' => $modules);
9011
      return;
9020
    }
903
    // Otherwise, install or enable the modules.
904
    else {
9050
      $dependencies = $form_storage['dependencies'];
9060
      foreach ($dependencies as $info) {
9070
        foreach ($info['dependencies'] as $dependency => $name) {
9080
          if (drupal_get_installed_schema_version($name) ==
SCHEMA_UNINSTALLED) {
9090
            $new_modules[$name] = $name;
9100
          }
911
          else {
9120
            $modules_to_be_enabled[$name] = $name;
913
          }
9140
        }
9150
      }
916
    }
9170
  }
918
  // If we have no dependencies, or the dependencies are confirmed
919
  // to be installed, we don't need the temporary storage anymore.
9203
  unset($form_state['storage']);
921
9223
  $old_module_list = module_list();
923
924
  // Enable the modules needing enabling.
9253
  if (!empty($modules_to_be_enabled)) {
9260
    module_enable($modules_to_be_enabled);
9270
  }
928
  // Disable the modules that need disabling.
9293
  if (!empty($disable_modules)) {
9301
    module_disable($disable_modules);
9311
  }
932
933
  // Install new modules.
9343
  if (!empty($new_modules)) {
9352
    foreach ($new_modules as $key => $module) {
9362
      if (!drupal_check_module($module)) {
9370
        unset($new_modules[$key]);
9380
      }
9392
    }
9402
    drupal_install_modules($new_modules);
9412
  }
942
9433
  $current_module_list = module_list(TRUE, FALSE);
9443
  if ($old_module_list != $current_module_list) {
9453
    drupal_set_message(t('The configuration options have been saved.'));
9463
  }
947
9483
  drupal_clear_css_cache();
9493
  drupal_clear_js_cache();
950
9513
  $form_state['redirect'] = 'admin/build/modules';
952
953
  // Notify locale module about module changes, so translations can be
954
  // imported. This might start a batch, and only return to the redirect
955
  // path after that.
9563
  module_invoke('locale', 'system_update', $new_modules);
957
958
  // Synchronize to catch any actions that were added or removed.
9593
  actions_synchronize();
960
9613
  return;
9620
}
963
964
/**
965
 * Uninstall functions
966
 */
967
968
/**
969
 * Builds a form of currently disabled modules.
970
 *
971
 * @ingroup forms
972
 * @see system_modules_uninstall_validate()
973
 * @see system_modules_uninstall_submit()
974
 * @param $form_state['values']
975
 *   Submitted form values.
976
 * @return
977
 *   A form array representing the currently disabled modules.
978
 */
97999
function system_modules_uninstall($form_state = NULL) {
980
  // Make sure the install API is available.
9813
  include_once DRUPAL_ROOT . '/includes/install.inc';
982
983
  // Display the confirm form if any modules have been submitted.
9843
  if (isset($form_state) && $confirm_form =
system_modules_uninstall_confirm_form($form_state['storage'])) {
9851
    return $confirm_form;
9860
  }
987
9883
  $form = array();
989
990
  // Pull all disabled modules from the system table.
9913
  $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);
9923
  while ($module = db_fetch_object($disabled_modules)) {
993
994
    // Grab the module info
9952
    $info = unserialize($module->info);
996
997
    // Load the .install file, and check for an uninstall hook.
998
    // If the hook exists, the module can be uninstalled.
9992
    module_load_install($module->name);
10002
    if (module_hook($module->name, 'uninstall')) {
10012
      $form['modules'][$module->name]['name'] = array('#markup' =>
$info['name'] ? $info['name'] : $module->name);
10022
      $form['modules'][$module->name]['description'] = array('#markup' =>
t($info['description']));
10032
      $options[$module->name] = '';
10042
    }
10052
  }
1006
1007
  // Only build the rest of the form if there are any modules available to
uninstall.
10083
  if (!empty($options)) {
10092
    $form['uninstall'] = array(
10102
      '#type' => 'checkboxes',
10112
      '#options' => $options,
1012
    );
10132
    $form['buttons']['submit'] = array(
10142
      '#type' => 'submit',
10152
      '#value' => t('Uninstall'),
1016
    );
10172
    $form['#action'] = url('admin/build/modules/uninstall/confirm');
10182
  }
1019
  else {
10201
    $form['modules'] = array();
1021
  }
1022
10233
  return $form;
10240
}
1025
1026
/**
1027
 * Confirm uninstall of selected modules.
1028
 *
1029
 * @ingroup forms
1030
 * @param $storage
1031
 *   An associative array of modules selected to be uninstalled.
1032
 * @return
1033
 *   A form array representing modules to confirm.
1034
 */
103599
function system_modules_uninstall_confirm_form($storage) {
1036
  // Nothing to build.
10373
  if (!isset($storage)) {
10383
    return;
10390
  }
1040
1041
  // Construct the hidden form elements and list items.
10421
  foreach (array_filter($storage['uninstall']) as $module => $value) {
10431
    $info = drupal_parse_info_file(dirname(drupal_get_filename('module',
$module)) . '/' . $module . '.info');
10441
    $uninstall[] = $info['name'];
10451
    $form['uninstall'][$module] = array('#type' => 'hidden',
10461
      '#value' => 1,
1047
    );
10481
  }
1049
1050
  // Display a confirm form if modules have been selected.
10511
  if (isset($uninstall)) {
10521
    $form['#confirmed'] = TRUE;
10531
    $form['uninstall']['#tree'] = TRUE;
10541
    $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));
10551
    $form = confirm_form(
10561
      $form,
10571
      t('Confirm uninstall'),
10581
      'admin/build/modules/uninstall',
10591
      t('Would you like to continue with uninstalling the above?'),
10601
      t('Uninstall'),
10611
      t('Cancel'));
10621
    return $form;
10630
  }
10640
}
1065
1066
/**
1067
 * Validates the submitted uninstall form.
1068
 */
106999
function system_modules_uninstall_validate($form, &$form_state) {
1070
  // Form submitted, but no modules selected.
10712
  if (!count(array_filter($form_state['values']['uninstall']))) {
10720
    drupal_set_message(t('No modules selected.'), 'error');
10730
    drupal_goto('admin/build/modules/uninstall');
10740
  }
10752
}
1076
1077
/**
1078
 * Processes the submitted uninstall form.
1079
 */
108099
function system_modules_uninstall_submit($form, &$form_state) {
1081
  // Make sure the install API is available.
10822
  include_once DRUPAL_ROOT . '/includes/install.inc';
1083
10842
  if (!empty($form['#confirmed'])) {
1085
    // Call the uninstall routine for each selected module.
10861
    $modules = array_keys($form_state['values']['uninstall']);
10871
    drupal_uninstall_modules($modules);
10881
    drupal_set_message(t('The selected modules have been uninstalled.'));
1089
10901
    unset($form_state['storage']);
10911
    $form_state['redirect'] = 'admin/build/modules/uninstall';
10921
  }
1093
  else {
10941
    $form_state['storage'] = $form_state['values'];
1095
  }
10962
}
1097
1098
/**
1099
 * Menu callback. Display blocked IP addresses.
1100
 */
110199
function system_ip_blocking() {
110217
  $output = '';
110317
  $rows = array();
110417
  $header = array(t('IP address'), t('Operations'));
110517
  $result = db_query('SELECT * FROM {blocked_ips}');
110617
  while ($ip = db_fetch_object($result)) {
110710
    $rows[] = array(
110810
      $ip->ip,
110910
      l(t('delete'), "admin/settings/ip-blocking/delete/$ip->iid"),
1110
    );
111110
  }
1112
111317
  $output .= drupal_get_form('system_ip_blocking_form');
1114
111515
  $output .= theme('table', $header, $rows);
1116
111715
  return $output;
11180
}
1119
1120
/**
1121
 * Define the form for blocking IP addresses.
1122
 *
1123
 * @ingroup forms
1124
 * @see system_ip_blocking_form_validate()
1125
 * @see system_ip_blocking_form_submit()
1126
 */
112799
function system_ip_blocking_form($form_state) {
112817
  $form['ip'] = array(
112917
    '#title' => t('IP address'),
113017
    '#type' => 'textfield',
113117
    '#size' => 64,
113217
    '#maxlength' => 32,
113317
    '#default_value' => arg(3),
113417
    '#description' => t('Enter a valid IP address.'),
1135
  );
113617
  $form['submit'] = array(
113717
    '#type' => 'submit',
113817
    '#value' => t('Save'),
1139
  );
114017
  $form['#submit'][] = 'system_ip_blocking_form_submit';
114117
  $form['#validate'][] = 'system_ip_blocking_form_validate';
114217
  return $form;
11430
}
1144
114599
function system_ip_blocking_form_validate($form, &$form_state) {
11466
  $ip = trim($form_state['values']['ip']);
11476
  if (db_result(db_query("SELECT * FROM {blocked_ips} WHERE ip = '%s'",
$ip))) {
11481
    form_set_error('ip', t('This IP address is already blocked.'));
11491
  }
11505
  elseif ($ip == ip_address()) {
11510
    form_set_error('ip', t('You may not block your own IP address.'));
11520
  }
11535
  elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) ==
FALSE) {
11543
    form_set_error('ip', t('Please enter a valid IP address.'));
11553
  }
11566
}
1157
115899
function system_ip_blocking_form_submit($form, &$form_state) {
11592
  $ip = trim($form_state['values']['ip']);
11602
  db_query("INSERT INTO {blocked_ips} (ip) VALUES ('%s')", $ip);
11612
  drupal_set_message(t('The IP address %ip has been blocked.', array('%ip'
=> $ip)));
11622
  $form_state['redirect'] = 'admin/settings/ip-blocking';
11632
  return;
11640
}
1165
1166
/**
1167
 * IP deletion confirm page.
1168
 *
1169
 * @see system_ip_blocking_delete_submit()
1170
 */
117199
function system_ip_blocking_delete(&$form_state, $iid) {
11723
  $form['blocked_ip'] = array(
11733
    '#type' => 'value',
11743
    '#value' => $iid,
1175
  );
11763
  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'));
11770
}
1178
1179
/**
1180
 * Process system_ip_blocking_delete form submissions.
1181
 */
118299
function system_ip_blocking_delete_submit($form, &$form_state) {
11831
  $blocked_ip = $form_state['values']['blocked_ip'];
11841
  db_query("DELETE FROM {blocked_ips} WHERE iid = %d", $blocked_ip['iid']);
11851
  watchdog('user', 'Deleted %ip', array('%ip' => $blocked_ip['ip']));
11861
  drupal_set_message(t('The IP address %ip was deleted.', array('%ip' =>
$blocked_ip['ip'])));
11871
  $form_state['redirect'] = 'admin/settings/ip-blocking';
11881
}
1189
1190
/**
1191
 * Form builder; The general site information form.
1192
 *
1193
 * @ingroup forms
1194
 * @see system_settings_form()
1195
 */
119699
function system_site_information_settings() {
11970
  $form['site_name'] = array(
11980
    '#type' => 'textfield',
11990
    '#title' => t('Name'),
12000
    '#default_value' => variable_get('site_name', 'Drupal'),
12010
    '#description' => t('The name of this website.'),
1202
    '#required' => TRUE
12030
  );
12040
  $form['site_mail'] = array(
12050
    '#type' => 'textfield',
12060
    '#title' => t('E-mail address'),
12070
    '#default_value' => variable_get('site_mail',
ini_get('sendmail_from')),
12080
    '#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.)"),
12090
    '#required' => TRUE,
1210
  );
12110
  $form['site_slogan'] = array(
12120
    '#type' => 'textfield',
12130
    '#title' => t('Slogan'),
12140
    '#default_value' => variable_get('site_slogan', ''),
12150
    '#description' => t("Your site's motto, tag line, or catchphrase (often
displayed alongside the title of the site).")
12160
  );
12170
  $form['site_mission'] = array(
12180
    '#type' => 'textarea',
12190
    '#title' => t('Mission'),
12200
    '#default_value' => variable_get('site_mission', ''),
12210
    '#description' => t("Your site's mission or focus statement (often
prominently displayed on the front page).")
12220
  );
12230
  $form['site_footer'] = array(
12240
    '#type' => 'textarea',
12250
    '#title' => t('Footer message'),
12260
    '#default_value' => variable_get('site_footer', ''),
12270
    '#description' => t('This text will be displayed at the bottom of each
page. Useful for adding a copyright notice to your pages.')
12280
  );
12290
  $form['anonymous'] = array(
12300
    '#type' => 'textfield',
12310
    '#title' => t('Anonymous user'),
12320
    '#default_value' => variable_get('anonymous', t('Anonymous')),
12330
    '#description' => t('The name used to indicate anonymous users.'),
12340
    '#required' => TRUE,
1235
  );
12360
  $form['site_frontpage'] = array(
12370
    '#type' => 'textfield',
12380
    '#title' => t('Default front page'),
12390
    '#default_value' => variable_get('site_frontpage', 'node'),
12400
    '#size' => 40,
12410
    '#description' => t('The home page displays content from this relative
URL. If unsure, specify "node".'),
12420
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) .
(variable_get('clean_url', 0) ? '' : '?q='),
12430
    '#required' => TRUE,
1244
  );
12450
  $form['#validate'][] = 'system_site_information_settings_validate';
1246
12470
  return system_settings_form($form);
12480
}
1249
1250
/**
1251
 * Validate the submitted site-information form.
1252
 */
125399
function system_site_information_settings_validate($form, &$form_state) {
1254
  // Validate the e-mail address.
12550
  if ($error = user_validate_mail($form_state['values']['site_mail'])) {
12560
    form_set_error('site_mail', $error);
12570
  }
1258
  // Validate front page path.
12590
  $item = array('link_path' => $form_state['values']['site_frontpage']);
12600
  $normal_path = drupal_get_normal_path($item['link_path']);
12610
  if ($item['link_path'] != $normal_path) {
12620
    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)));
12630
    $item['link_path'] = $normal_path;
12640
  }
12650
  if (!empty($item) && !menu_valid_path($item)) {
12660
    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'])));
12670
  }
12680
}
1269
1270
/**
1271
 * Form builder; Configure error reporting settings.
1272
 *
1273
 * @ingroup forms
1274
 * @see system_settings_form()
1275
 */
127699
function system_error_reporting_settings() {
1277
127812
  $form['site_403'] = array(
127912
    '#type' => 'textfield',
128012
    '#title' => t('Default 403 (access denied) page'),
128112
    '#default_value' => variable_get('site_403', ''),
128212
    '#size' => 40,
128312
    '#description' => t('This page is displayed when the requested document
is denied to the current user. If unsure, specify nothing.'),
128412
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) .
(variable_get('clean_url', 0) ? '' : '?q=')
128512
  );
1286
128712
  $form['site_404'] = array(
128812
    '#type' => 'textfield',
128912
    '#title' => t('Default 404 (not found) page'),
129012
    '#default_value' => variable_get('site_404', ''),
129112
    '#size' => 40,
129212
    '#description' => t('This page is displayed when no other content
matches the requested document. If unsure, specify nothing.'),
129312
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) .
(variable_get('clean_url', 0) ? '' : '?q=')
129412
  );
1295
129612
  $form['error_level'] = array(
129712
    '#type' => 'select', '#title' => t('Error reporting'), '#default_value'
=> variable_get('error_level', 1),
129812
    '#options' => array(t('Write errors to the log'), t('Write errors to
the log and to the screen')),
129912
    '#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.')
130012
  );
1301
130212
  return system_settings_form($form);
13030
}
1304
1305
/**
1306
 * Menu callback; Menu page for the various logging options.
1307
 */
130899
function system_logging_overview() {
13090
  $item = menu_get_item('admin/settings/logging');
13100
  $content = system_admin_menu_block($item);
1311
13120
  $output = theme('admin_block_content', $content);
1313
13140
  return $output;
13150
}
1316
1317
/**
1318
 * Form builder; Configure site performance settings.
1319
 *
1320
 * @ingroup forms
1321
 * @see system_settings_form()
1322
 */
132399
function system_performance_settings() {
1324
13250
  $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>';
1326
13270
  $problem_modules = array_unique(array_merge(module_implements('boot'),
module_implements('exit')));
13280
  sort($problem_modules);
1329
13300
  if (count($problem_modules) > 0) {
13310
    $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>';
13320
  }
1333
  else {
13340
    $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>';
1335
  }
13360
  $form['page_cache'] = array(
13370
    '#type' => 'fieldset',
13380
    '#title' => t('Page cache'),
13390
    '#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.'),
1340
  );
1341
13420
  $form['page_cache']['cache'] = array(
13430
    '#type' => 'radios',
13440
    '#title' => t('Caching mode'),
13450
    '#default_value' => variable_get('cache', CACHE_DISABLED),
13460
    '#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)')),
1347
    '#description' => $description
13480
  );
1349
13500
  $period = drupal_map_assoc(array(0, 60, 180, 300, 600, 900, 1800, 2700,
3600, 10800, 21600, 32400, 43200, 86400), 'format_interval');
13510
  $period[0] = '<' . t('none') . '>';
13520
  $form['page_cache']['cache_lifetime'] = array(
13530
    '#type' => 'select',
13540
    '#title' => t('Minimum cache lifetime'),
13550
    '#default_value' => variable_get('cache_lifetime', 0),
13560
    '#options' => $period,
13570
    '#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.')
13580
  );
13590
  $form['page_cache']['page_compression'] = array(
13600
    '#type' => 'radios',
13610
    '#title' => t('Page compression'),
13620
    '#default_value' => variable_get('page_compression', TRUE),
13630
    '#options' => array(t('Disabled'), t('Enabled')),
13640
    '#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."),
1365
  );
1366
13670
  $form['block_cache'] = array(
13680
    '#type' => 'fieldset',
13690
    '#title' => t('Block cache'),
13700
    '#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.'),
1371
  );
1372
13730
  $form['block_cache']['block_cache'] = array(
13740
    '#type' => 'radios',
13750
    '#title' => t('Block cache'),
13760
    '#default_value' => variable_get('block_cache', CACHE_DISABLED),
13770
    '#options' => array(CACHE_DISABLED => t('Disabled'), CACHE_NORMAL =>
t('Enabled (recommended)')),
13780
    '#disabled' => count(module_implements('node_grants')),
13790
    '#description' => t('Note that block caching is inactive when modules
defining content access restrictions are enabled.'),
1380
  );
1381
13820
  $form['bandwidth_optimizations'] = array(
13830
    '#type' => 'fieldset',
13840
    '#title' => t('Bandwidth optimizations'),
13850
    '#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>'
13860
  );
1387
13880
  $directory = file_directory_path();
13890
  $is_writable = is_dir($directory) && is_writable($directory) &&
(variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) ==
FILE_DOWNLOADS_PUBLIC);
13900
  $form['bandwidth_optimizations']['preprocess_css'] = array(
13910
    '#type' => 'radios',
13920
    '#title' => t('Optimize CSS files'),
13930
    '#default_value' => intval(variable_get('preprocess_css', 0) &&
$is_writable),
13940
    '#disabled' => !$is_writable,
13950
    '#options' => array(t('Disabled'), t('Enabled')),
13960
    '#description' => t('This option can interfere with theme development
and should only be enabled in a production environment.'),
1397
  );
13980
  $form['bandwidth_optimizations']['preprocess_js'] = array(
13990
    '#type' => 'radios',
14000
    '#title' => t('Optimize JavaScript files'),
14010
    '#default_value' => intval(variable_get('preprocess_js', 0) &&
$is_writable),
14020
    '#disabled' => !$is_writable,
14030
    '#options' => array(t('Disabled'), t('Enabled')),
14040
    '#description' => t('This option can interfere with module development
and should only be enabled in a production environment.'),
1405
  );
1406
14070
  $form['clear_cache'] = array(
14080
    '#type' => 'fieldset',
14090
    '#title' => t('Clear cached data'),
14100
    '#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>'),
1411
  );
1412
14130
  $form['clear_cache']['clear'] = array(
14140
    '#type' => 'submit',
14150
    '#value' => t('Clear cached data'),
14160
    '#submit' => array('system_clear_cache_submit'),
1417
  );
1418
14190
  $form['#submit'][] = 'drupal_clear_css_cache';
14200
  $form['#submit'][] = 'drupal_clear_js_cache';
1421
14220
  return system_settings_form($form);
14230
}
1424
1425
/**
1426
 * Submit callback; clear system caches.
1427
 *
1428
 * @ingroup forms
1429
 */
143099
function system_clear_cache_submit(&$form_state, $form) {
14310
  drupal_flush_all_caches();
14320
  drupal_set_message(t('Caches cleared.'));
14330
}
1434
1435
/**
1436
 * Form builder; Configure the site file handling.
1437
 *
1438
 * @ingroup forms
1439
 * @see system_settings_form()
1440
 */
144199
function system_file_system_settings() {
1442
14430
  $form['file_directory_path'] = array(
14440
    '#type' => 'textfield',
14450
    '#title' => t('File system path'),
14460
    '#default_value' => file_directory_path(),
14470
    '#maxlength' => 255,
14480
    '#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.'),
14490
    '#after_build' => array('system_check_directory'),
1450
  );
1451
14520
  $form['file_directory_temp'] = array(
14530
    '#type' => 'textfield',
14540
    '#title' => t('Temporary directory'),
14550
    '#default_value' => file_directory_temp(),
14560
    '#maxlength' => 255,
14570
    '#description' => t('A file system path where uploaded files will be
stored during previews.'),
14580
    '#after_build' => array('system_check_directory'),
1459
  );
1460
14610
  $form['file_downloads'] = array(
14620
    '#type' => 'radios',
14630
    '#title' => t('Download method'),
14640
    '#default_value' => variable_get('file_downloads',
FILE_DOWNLOADS_PUBLIC),
14650
    '#options' => array(FILE_DOWNLOADS_PUBLIC => t('Public - files are
available using HTTP directly.'), FILE_DOWNLOADS_PRIVATE => t('Private -
files are transferred by Drupal.')),
14660
    '#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.')
14670
  );
1468
14690
  return system_settings_form($form);
14700
}
1471
1472
/**
1473
 * Form builder; Configure site image toolkit usage.
1474
 *
1475
 * @ingroup forms
1476
 * @see system_settings_form()
1477
 */
147899
function system_image_toolkit_settings() {
14790
  $toolkits_available = image_get_available_toolkits();
14800
  if (count($toolkits_available) > 1) {
14810
    $form['image_toolkit'] = array(
14820
      '#type' => 'radios',
14830
      '#title' => t('Select an image processing toolkit'),
14840
      '#default_value' => variable_get('image_toolkit',
image_get_toolkit()),
1485
      '#options' => $toolkits_available
14860
    );
14870
  }
14880
  elseif (count($toolkits_available) == 1) {
14890
    variable_set('image_toolkit', key($toolkits_available));
14900
  }
1491
14920
  $form['image_toolkit_settings'] = image_toolkit_invoke('settings');
1493
14940
  return system_settings_form($form);
14950
}
1496
1497
/**
1498
 * Form builder; Configure how the site handles RSS feeds.
1499
 *
1500
 * @ingroup forms
1501
 * @see system_settings_form()
1502
 */
150399
function system_rss_feeds_settings() {
1504
15050
  $form['feed_default_items'] = array(
15060
    '#type' => 'select',
15070
    '#title' => t('Number of items in each feed'),
15080
    '#default_value' => variable_get('feed_default_items', 10),
15090
    '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15,
20, 25, 30)),
15100
    '#description' => t('Default number of items to include in each feed.')
15110
  );
15120
  $form['feed_item_length'] = array(
15130
    '#type' => 'select',
15140
    '#title' => t('Feed content'),
15150
    '#default_value' => variable_get('feed_item_length', 'teaser'),
15160
    '#options' => array('title' => t('Titles only'), 'teaser' => t('Titles
plus teaser'), 'fulltext' => t('Full text')),
15170
    '#description' => t('Global setting for the default display of content
items in each feed.')
15180
  );
1519
15200
  return system_settings_form($form);
15210
}
1522
1523
/**
1524
 * Form builder; Configure the site date and time settings.
1525
 *
1526
 * @ingroup forms
1527
 * @see system_settings_form()
1528
 * @see system_date_time_settings_submit()
1529
 */
153099
function system_date_time_settings() {
15310
  drupal_add_js(drupal_get_path('module', 'system') . '/system.js',
'module');
15320
  drupal_add_js(array('dateTime' => array('lookup' =>
url('admin/settings/date-time/lookup'))), 'setting');
1533
1534
  // Date settings:
15350
  $zones = _system_zonelist();
1536
1537
  // Date settings: possible date formats
15380
  $date_short = array('Y-m-d H:i', 'm/d/Y - H:i', 'd/m/Y - H:i', 'Y/m/d -
H:i',
15390
           'd.m.Y - H:i', 'm/d/Y - g:ia', 'd/m/Y - g:ia', 'Y/m/d - g:ia',
15400
           'M j Y - H:i', 'j M Y - H:i', 'Y M j - H:i',
15410
           'M j Y - g:ia', 'j M Y - g:ia', 'Y M j - g:ia');
15420
  $date_medium = array('D, Y-m-d H:i', 'D, m/d/Y - H:i', 'D, d/m/Y - H:i',
15430
          'D, Y/m/d - H:i', 'F j, Y - H:i', 'j F, Y - H:i', 'Y, F j - H:i',
15440
          'D, m/d/Y - g:ia', 'D, d/m/Y - g:ia', 'D, Y/m/d - g:ia',
15450
          'F j, Y - g:ia', 'j F Y - g:ia', 'Y, F j - g:ia', 'j. F Y -
G:i');
15460
  $date_long = array('l, F j, Y - H:i', 'l, j F, Y - H:i', 'l, Y, F j -
H:i',
15470
        'l, F j, Y - g:ia', 'l, j F Y - g:ia', 'l, Y, F j - g:ia', 'l, j. F
Y - G:i');
1548
1549
  // Date settings: construct choices for user
15500
  foreach ($date_short as $f) {
15510
    $date_short_choices[$f] = format_date(REQUEST_TIME, 'custom', $f);
15520
  }
15530
  foreach ($date_medium as $f) {
15540
    $date_medium_choices[$f] = format_date(REQUEST_TIME, 'custom', $f);
15550
  }
15560
  foreach ($date_long as $f) {
15570
    $date_long_choices[$f] = format_date(REQUEST_TIME, 'custom', $f);
15580
  }
1559
15600
  $date_long_choices['custom'] = $date_medium_choices['custom'] =
$date_short_choices['custom'] = t('Custom format');
1561
15620
  $form['locale'] = array(
15630
    '#type' => 'fieldset',
15640
    '#title' => t('Locale settings'),
1565
  );
1566
15670
  $form['locale']['date_default_timezone'] = array(
15680
    '#type' => 'select',
15690
    '#title' => t('Default time zone'),
15700
    '#default_value' => variable_get('date_default_timezone', 0),
15710
    '#options' => $zones,
15720
    '#description' => t('Select the default site time zone.')
15730
  );
1574
15750
  $form['locale']['configurable_timezones'] = array(
15760
    '#type' => 'radios',
15770
    '#title' => t('User-configurable time zones'),
15780
    '#default_value' => variable_get('configurable_timezones', 1),
15790
    '#options' => array(t('Disabled'), t('Enabled')),
15800
    '#description' => t('When enabled, users can set their own time zone
and dates will be displayed accordingly.')
15810
  );
1582
15830
  $form['locale']['date_first_day'] = array(
15840
    '#type' => 'select',
15850
    '#title' => t('First day of week'),
15860
    '#default_value' => variable_get('date_first_day', 0),
15870
    '#options' => array(0 => t('Sunday'), 1 => t('Monday'), 2 =>
t('Tuesday'), 3 => t('Wednesday'), 4 => t('Thursday'), 5 => t('Friday'), 6
=> t('Saturday')),
15880
    '#description' => t('The first day of the week for calendar views.')
15890
  );
1590
15910
  $form['date_formats'] = array(
15920
    '#type' => 'fieldset',
15930
    '#title' => t('Formatting'),
1594
  );
1595
15960
  $date_format_short = variable_get('date_format_short', $date_short[1]);
15970
  $form['date_formats']['date_format_short'] = array(
15980
    '#prefix' => '<div class="date-container"><div
class="select-container">',
15990
    '#suffix' => '</div>',
16000
    '#type' => 'select',
16010
    '#title' => t('Short date format'),
16020
    '#attributes' => array('class' => 'date-format'),
16030
    '#default_value' => (isset($date_short_choices[$date_format_short]) ?
$date_format_short : 'custom'),
16040
    '#options' => $date_short_choices,
16050
    '#description' => t('The short format of date display.'),
1606
  );
1607
16080
  $default_short_custom = variable_get('date_format_short_custom',
(isset($date_short_choices[$date_format_short]) ? $date_format_short :
''));
16090
  $form['date_formats']['date_format_short_custom'] = array(
16100
    '#prefix' => '<div class="custom-container">',
16110
    '#suffix' => '</div></div>',
16120
    '#type' => 'textfield',
16130
    '#title' => t('Custom short date format'),
16140
    '#attributes' => array('class' => 'custom-format'),
16150
    '#default_value' => $default_short_custom,
16160
    '#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(REQUEST_TIME, 'custom', $default_short_custom))),
1617
  );
1618
16190
  $date_format_medium = variable_get('date_format_medium',
$date_medium[1]);
16200
  $form['date_formats']['date_format_medium'] = array(
16210
    '#prefix' => '<div class="date-container"><div
class="select-container">',
16220
    '#suffix' => '</div>',
16230
    '#type' => 'select',
16240
    '#title' => t('Medium date format'),
16250
    '#attributes' => array('class' => 'date-format'),
16260
    '#default_value' => (isset($date_medium_choices[$date_format_medium]) ?
$date_format_medium : 'custom'),
16270
    '#options' => $date_medium_choices,
16280
    '#description' => t('The medium sized date display.'),
1629
  );
1630
16310
  $default_medium_custom = variable_get('date_format_medium_custom',
(isset($date_medium_choices[$date_format_medium]) ? $date_format_medium :
''));
16320
  $form['date_formats']['date_format_medium_custom'] = array(
16330
    '#prefix' => '<div class="custom-container">',
16340
    '#suffix' => '</div></div>',
16350
    '#type' => 'textfield',
16360
    '#title' => t('Custom medium date format'),
16370
    '#attributes' => array('class' => 'custom-format'),
16380
    '#default_value' => $default_medium_custom,
16390
    '#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(REQUEST_TIME, 'custom', $default_medium_custom))),
1640
  );
1641
16420
  $date_format_long = variable_get('date_format_long', $date_long[0]);
16430
  $form['date_formats']['date_format_long'] = array(
16440
    '#prefix' => '<div class="date-container"><div
class="select-container">',
16450
    '#suffix' => '</div>',
16460
    '#type' => 'select',
16470
    '#title' => t('Long date format'),
16480
    '#attributes' => array('class' => 'date-format'),
16490
    '#default_value' => (isset($date_long_choices[$date_format_long]) ?
$date_format_long : 'custom'),
16500
    '#options' => $date_long_choices,
16510
    '#description' => t('Longer date format used for detailed display.')
16520
  );
1653
16540
  $default_long_custom = variable_get('date_format_long_custom',
(isset($date_long_choices[$date_format_long]) ? $date_format_long : ''));
16550
  $form['date_formats']['date_format_long_custom'] = array(
16560
    '#prefix' => '<div class="custom-container">',
16570
    '#suffix' => '</div></div>',
16580
    '#type' => 'textfield',
16590
    '#title' => t('Custom long date format'),
16600
    '#attributes' => array('class' => 'custom-format'),
16610
    '#default_value' => $default_long_custom,
16620
    '#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(REQUEST_TIME, 'custom', $default_long_custom))),
1663
  );
1664
16650
  $form = system_settings_form($form);
1666
  // We will call system_settings_form_submit() manually, so remove it for
now.
16670
  unset($form['#submit']);
16680
  return $form;
16690
}
1670
1671
/**
1672
 * Process system_date_time_settings form submissions.
1673
 */
167499
function system_date_time_settings_submit($form, &$form_state) {
16750
  if ($form_state['values']['date_format_short'] == 'custom') {
16760
    $form_state['values']['date_format_short'] =
$form_state['values']['date_format_short_custom'];
16770
  }
16780
  if ($form_state['values']['date_format_medium'] == 'custom') {
16790
    $form_state['values']['date_format_medium'] =
$form_state['values']['date_format_medium_custom'];
16800
  }
16810
  if ($form_state['values']['date_format_long'] == 'custom') {
16820
    $form_state['values']['date_format_long'] =
$form_state['values']['date_format_long_custom'];
16830
  }
16840
  return system_settings_form_submit($form, $form_state);
16850
}
1686
1687
/**
1688
 * Return the date for a given format string via Ajax.
1689
 */
169099
function system_date_time_lookup() {
16910
  $result = format_date(REQUEST_TIME, 'custom', $_GET['format']);
16920
  echo drupal_to_js($result);
16930
  exit;
16940
}
1695
1696
/**
1697
 * Form builder; Configure the site's maintenance status.
1698
 *
1699
 * @ingroup forms
1700
 * @see system_settings_form()
1701
 */
170299
function system_site_maintenance_settings() {
1703
17040
  $form['site_offline'] = array(
17050
    '#type' => 'radios',
17060
    '#title' => t('Site status'),
17070
    '#default_value' => variable_get('site_offline', 0),
17080
    '#options' => array(t('Online'), t('Offline')),
17090
    '#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'))),
1710
  );
1711
17120
  $form['site_offline_message'] = array(
17130
    '#type' => 'textarea',
17140
    '#title' => t('Site offline message'),
17150
    '#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')))),
17160
    '#description' => t('Message to show visitors when the site is in
offline mode.')
17170
  );
1718
17190
  return system_settings_form($form);
17200
}
1721
1722
/**
1723
 * Form builder; Configure Clean URL settings.
1724
 *
1725
 * @ingroup forms
1726
 * @see system_settings_form()
1727
 */
172899
function system_clean_url_settings() {
17290
  $form['clean_url'] = array(
17300
    '#type' => 'radios',
17310
    '#title' => t('Clean URLs'),
17320
    '#default_value' => variable_get('clean_url', 0),
17330
    '#options' => array(t('Disabled'), t('Enabled')),
17340
    '#description' => t('This option makes Drupal emit "clean" URLs (i.e.
without <code>?q=</code> in the URL).'),
1735
  );
1736
17370
  if (!variable_get('clean_url', 0)) {
17380
    if (strpos(request_uri(), '?q=') !== FALSE) {
17390
      drupal_add_js(drupal_get_path('module', 'system') . '/system.js',
'module');
1740
17410
      $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>';
1742
17430
      $form['clean_url']['#disabled'] = TRUE;
17440
      $form['clean_url']['#prefix'] = '<div id="clean-url">';
17450
      $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>';
17460
    }
1747
    else {
17480
      $form['clean_url']['#description'] .= ' <div class="ok">' . t('Your
server has been successfully tested to support this feature.') . '</div>';
1749
    }
17500
  }
1751
17520
  return system_settings_form($form);
17530
}
1754
1755
/**
1756
 * Menu callback: displays the site status report. Can also be used as a
pure check.
1757
 *
1758
 * @param $check
1759
 *   If true, only returns a boolean whether there are system status
errors.
1760
 */
176199
function system_status($check = FALSE) {
1762
  // Load .install files
17633
  include_once DRUPAL_ROOT . '/includes/install.inc';
17643
  drupal_load_updates();
1765
1766
  // Check run-time requirements and status information.
17673
  $requirements = module_invoke_all('requirements', 'runtime');
17683
  usort($requirements, '_system_sort_requirements');
1769
17703
  if ($check) {
17712
    return drupal_requirements_severity($requirements) ==
REQUIREMENT_ERROR;
17720
  }
1773
  // MySQL import might have set the uid of the anonymous user to
autoincrement
1774
  // value. Let's try fixing it. See http://drupal.org/node/204411
17751
  db_query("UPDATE {users} SET uid = uid - uid WHERE name = '' AND pass =
'' AND status = 0");
1776
17771
  return theme('status_report', $requirements);
17780
}
1779
1780
/**
1781
 * Menu callback: run cron manually.
1782
 */
178399
function system_run_cron() {
1784
  // Run cron manually
17851
  if (drupal_cron_run()) {
17861
    drupal_set_message(t('Cron ran successfully.'));
17871
  }
1788
  else {
17890
    drupal_set_message(t('Cron run failed.'), 'error');
1790
  }
1791
17921
  drupal_goto('admin/reports/status');
17930
}
1794
1795
/**
1796
 * Menu callback: return information about PHP.
1797
 */
179899
function system_php() {
17990
  phpinfo(INFO_ALL);
18000
  exit();
18010
}
1802
1803
/**
1804
 * Theme a SQL result table.
1805
 *
1806
 * @param $data
1807
 *   The actual table data.
1808
 * @param $keys
1809
 *   Data keys and descriptions.
1810
 * @return
1811
 *   The output HTML.
1812
 */
181399
function _system_sql($data, $keys) {
18140
  $rows = array();
18150
  foreach ($keys as $key => $explanation) {
18160
    if (isset($data[$key])) {
18170
      $rows[] = array(check_plain($key), check_plain($data[$key]),
$explanation);
18180
    }
18190
  }
1820
18210
  return theme('table', array(t('Variable'), t('Value'), t('Description')),
$rows);
18220
}
1823
1824
/**
1825
 * Menu callback: return information about the database.
1826
 */
182799
function system_sql() {
1828
18290
  $result = db_query("SHOW STATUS");
18300
  while ($entry = db_fetch_object($result)) {
1831
    // 'SHOW STATUS' returns fields named 'Variable_name' and 'Value',
1832
    // case is important.
18330
    $data[$entry->Variable_name] = $entry->Value;
18340
  }
1835
18360
  $output  = '<h2>' . t('Command counters') . '</h2>';
18370
  $output .= _system_sql($data, array(
18380
   'Com_select' => t('The number of !sql statements.', array('!sql' =>
'<code>SELECT</code>')),
18390
   'Com_insert' => t('The number of !sql statements.', array('!sql' =>
'<code>INSERT</code>')),
18400
   'Com_update' => t('The number of !sql statements.', array('!sql' =>
'<code>UPDATE</code>')),
18410
   'Com_delete' => t('The number of !sql statements.', array('!sql' =>
'<code>DELETE</code>')),
18420
   'Com_lock_tables' => t('The number of table locks.'),
18430
   'Com_unlock_tables' => t('The number of table unlocks.')
18440
  ));
1845
18460
  $output .= '<h2>' . t('Query performance') . '</h2>';
18470
  $output .= _system_sql($data, array(
18480
   'Select_full_join' => t('The number of joins without an index; should be
zero.'),
18490
   'Select_range_check' => t('The number of joins without keys that check
for key usage after each row; should be zero.'),
18500
   'Sort_scan' => t('The number of sorts done without using an index;
should be zero.'),
18510
   'Table_locks_immediate' => t('The number of times a lock could be
acquired immediately.'),
18520
   'Table_locks_waited' => t('The number of times the server had to wait
for a lock.')
18530
  ));
1854
18550
  $output .= '<h2>' . t('Query cache information') . '</h2>';
18560
  $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>';
18570
  $output .= _system_sql($data, array(
18580
   'Qcache_queries_in_cache' => t('The number of queries in the query
cache.'),
18590
   'Qcache_hits' => t('The number of times MySQL found previous results in
the cache.'),
18600
   'Qcache_inserts' => t('The number of times MySQL added a query to the
cache (misses).'),
18610
   '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.')
18620
  ));
1863
18640
  return $output;
18650
}
1866
1867
/**
1868
 * Default page callback for batches.
1869
 */
187099
function system_batch_page() {
18718
  require_once DRUPAL_ROOT . '/includes/batch.inc';
18728
  $output = _batch_page();
18734
  if ($output === FALSE) {
18740
    drupal_access_denied();
18750
  }
18764
  elseif (isset($output)) {
1877
    // Force a page without blocks or messages to
1878
    // display a list of collected messages later.
18794
    print theme('page', $output, FALSE, FALSE);
18804
  }
18814
}
1882
1883
/**
1884
 * This function formats an administrative block for display.
1885
 *
1886
 * @param $block
1887
 *   An array containing information about the block. It should
1888
 *   include a 'title', a 'description' and a formatted 'content'.
1889
 * @ingroup themeable
1890
 */
189199
function theme_admin_block($block) {
1892
  // Don't display the block if it has no content to display.
18933
  if (empty($block['content'])) {
18941
    return '';
18950
  }
1896
1897
  $output = <<< EOT
18982
  <div class="admin-panel">
1899
    <h3>
19002
      $block[title]
1901
    </h3>
1902
    <div class="body">
1903
      <p class="description">
19042
        $block[description]
1905
      </p>
19062
      $block[content]
1907
    </div>
19082
  </div>
19092
EOT;
19102
  return $output;
19110
}
1912
1913
/**
1914
 * This function formats the content of an administrative block.
1915
 *
1916
 * @param $block
1917
 *   An array containing information about the block. It should
1918
 *   include a 'title', a 'description' and a formatted 'content'.
1919
 * @ingroup themeable
1920
 */
192199
function theme_admin_block_content($content) {
19224
  if (!$content) {
19231
    return '';
19240
  }
1925
19263
  if (system_admin_compact_mode()) {
19270
    $output = '<ul class="menu">';
19280
    foreach ($content as $item) {
19290
      $output .= '<li class="leaf">' . l($item['title'], $item['href'],
$item['localized_options']) . '</li>';
19300
    }
19310
    $output .= '</ul>';
19320
  }
1933
  else {
19343
    $output = '<dl class="admin-list">';
19353
    foreach ($content as $item) {
19363
      $output .= '<dt>' . l($item['title'], $item['href'],
$item['localized_options']) . '</dt>';
19373
      $output .= '<dd>' . $item['description'] . '</dd>';
19383
    }
19393
    $output .= '</dl>';
1940
  }
19413
  return $output;
19420
}
1943
1944
/**
1945
 * This function formats an administrative page for viewing.
1946
 *
1947
 * @param $blocks
1948
 *   An array of blocks to display. Each array should include a
1949
 *   'title', a 'description', a formatted 'content' and a
1950
 *   'position' which will control which container it will be
1951
 *   in. This is usually 'left' or 'right'.
1952
 * @ingroup themeable
1953
 */
195499
function theme_admin_page($blocks) {
19552
  $stripe = 0;
19562
  $container = array();
1957
19582
  foreach ($blocks as $block) {
19592
    if ($block_output = theme('admin_block', $block)) {
19601
      if (empty($block['position'])) {
1961
        // perform automatic striping.
19620
        $block['position'] = ++$stripe % 2 ? 'left' : 'right';
19630
      }
19641
      if (!isset($container[$block['position']])) {
19651
        $container[$block['position']] = '';
19661
      }
19671
      $container[$block['position']] .= $block_output;
19681
    }
19692
  }
1970
19712
  $output = '<div class="admin clear-block">';
19722
  $output .= theme('system_compact_link');
1973
19742
  foreach ($container as $id => $data) {
19751
    $output .= '<div class="' . $id . ' clear-block">';
19761
    $output .= $data;
19771
    $output .= '</div>';
19781
  }
19792
  $output .= '</div>';
19802
  return $output;
19810
}
1982
1983
/**
1984
 * Theme output of the dashboard page.
1985
 *
1986
 * @param $menu_items
1987
 *   An array of modules to be displayed.
1988
 * @ingroup themeable
1989
 */
199099
function theme_system_admin_by_module($menu_items) {
19912
  $stripe = 0;
19922
  $output = '';
19932
  $container = array('left' => '', 'right' => '');
19942
  $flip = array('left' => 'right', 'right' => 'left');
19952
  $position = 'left';
1996
1997
  // Iterate over all modules
19982
  foreach ($menu_items as $module => $block) {
19991
    list($description, $items) = $block;
2000
2001
    // Output links
20021
    if (count($items)) {
20031
      $block = array();
20041
      $block['title'] = $module;
20051
      $block['content'] = theme('item_list', $items);
20061
      $block['description'] = t($description);
2007
20081
      if ($block_output = theme('admin_block', $block)) {
20091
        if (!isset($block['position'])) {
2010
          // Perform automatic striping.
20111
          $block['position'] = $position;
20121
          $position = $flip[$position];
20131
        }
20141
        $container[$block['position']] .= $block_output;
20151
      }
20161
    }
20171
  }
2018
20192
  $output = '<div class="admin clear-block">';
20202
  foreach ($container as $id => $data) {
20212
    $output .= '<div class="' . $id . ' clear-block">';
20222
    $output .= $data;
20232
    $output .= '</div>';
20242
  }
20252
  $output .= '</div>';
2026
20272
  return $output;
20280
}
2029
2030
/**
2031
 * Theme requirements status report.
2032
 *
2033
 * @param $requirements
2034
 *   An array of requirements.
2035
 * @ingroup themeable
2036
 */
203799
function theme_status_report(&$requirements) {
20381
  $i = 0;
20391
  $output = '<table class="system-status-report">';
20401
  foreach ($requirements as $requirement) {
20411
    if (empty($requirement['#type'])) {
20421
      $class = ++$i % 2 == 0 ? 'even' : 'odd';
2043
2044
      $classes = array(
20451
        REQUIREMENT_INFO => 'info',
20461
        REQUIREMENT_OK => 'ok',
20471
        REQUIREMENT_WARNING => 'warning',
20481
        REQUIREMENT_ERROR => 'error',
20491
      );
20501
      $class = $classes[isset($requirement['severity']) ?
(int)$requirement['severity'] : 0] . ' ' . $class;
2051
2052
      // Output table row(s)
20531
      if (!empty($requirement['description'])) {
20541
        $output .= '<tr class="' . $class . ' merge-down"><th>' .
$requirement['title'] . '</th><td>' . $requirement['value'] . '</td></tr>';
20551
        $output .= '<tr class="' . $class . ' merge-up"><td colspan="2">' .
$requirement['description'] . '</td></tr>';
20561
      }
2057
      else {
20581
        $output .= '<tr class="' . $class . '"><th>' .
$requirement['title'] . '</th><td>' . $requirement['value'] . '</td></tr>';
2059
      }
20601
    }
20611
  }
2062
20631
  $output .= '</table>';
20641
  return $output;
20650
}
2066
2067
/**
2068
 * Theme callback for the modules form.
2069
 *
2070
 * @param $form
2071
 *   An associative array containing the structure of the form.
2072
 * @ingroup themeable
2073
 */
207499
function theme_system_modules_fieldset($form) {
2075
  // Individual table headers.
20768
  $rows = array();
2077
  // Iterate through all the modules, which are
2078
  // children of this fieldset.
20798
  foreach (element_children($form) as $key) {
2080
    // Stick it into $module for easier accessing.
20818
    $module = $form[$key];
20828
    $row = array();
20838
    unset($module['enable']['#title']);
20848
    $row[] = array('class' => 'checkbox', 'data' =>
drupal_render($module['enable']));
20858
    $label = '<label';
20868
    if (isset($module['enable']['#id'])) {
20878
      $label .= ' for="' . $module['enable']['#id'] . '"';
20888
    }
20898
    $row[] = $label . '><strong>' . drupal_render($module['name']) .
'</strong></label>';
20908
    $row[] = drupal_render($module['version']);
20918
    $description = '';
2092
    // If we have help, it becomes the first part
2093
    // of the description - with CSS, it is float: right'd.
20948
    if (isset($module['help'])) {
20958
      $description = '<div class="module-help">'.
drupal_render($module['help']) .'</div>';
20968
    }
2097
    // Add the description, along with any dependencies.
20988
    $description .= drupal_render($module['description']);
20998
    if ($module['#dependencies']) {
21008
     $description .= '<div class="admin-dependencies">' . t('Depends on: ')
. implode(', ', $module['#dependencies']) . '</div>';
21018
    }
21028
    if ($module['#dependents']) {
21038
     $description .= '<div class="admin-dependencies">' . t('Required by:
') . implode(', ', $module['#dependents']) . '</div>';
21048
    }
21058
    $row[] = array('data' => $description, 'class' => 'description');
21068
    $rows[] = $row;
21078
  }
2108
21098
  return theme('table', $form['#header'], $rows);
21100
}
2111
2112
/**
2113
 * Themes an incompatible message.
2114
 *
2115
 * @ingroup themeable
2116
 * @param $message
2117
 *   The form array representing the currently disabled modules.
2118
 * @return
2119
 *   An HTML string for the message.
2120
 */
212199
function theme_system_modules_incompatible($message) {
21220
  return '<div class="incompatible">'. $message .'</div>';
21230
}
2124
2125
/**
2126
 * Themes a table of currently disabled modules.
2127
 *
2128
 * @ingroup themeable
2129
 * @param $form
2130
 *   The form array representing the currently disabled modules.
2131
 * @return
2132
 *   An HTML string representing the table.
2133
 */
213499
function theme_system_modules_uninstall($form) {
2135
  // No theming for the confirm form.
21362
  if (isset($form['confirm'])) {
21370
    return drupal_render($form);
21380
  }
2139
2140
  // Table headers.
21412
  $header = array(t('Uninstall'),
21422
    t('Name'),
21432
    t('Description'),
21442
  );
2145
2146
  // Display table.
21472
  $rows = array();
21482
  foreach (element_children($form['modules']) as $module) {
21491
    $rows[] = array(
21501
      array('data' => drupal_render($form['uninstall'][$module]), 'align'
=> 'center'),
21511
      '<strong><label for="' . $form['uninstall'][$module]['#id'] . '">' .
drupal_render($form['modules'][$module]['name']) . '</label></strong>',
21521
      array('data' =>
drupal_render($form['modules'][$module]['description']), 'class' =>
'description'),
2153
    );
21541
  }
2155
2156
  // Only display table if there are modules that can be uninstalled.
21572
  if (empty($rows)) {
21581
    $rows[] = array(array('data' => t('No modules are available to
uninstall.'), 'colspan' => '3', 'align' => 'center', 'class' =>
'message'));
21591
  }
2160
21612
  $output  = theme('table', $header, $rows);
21622
  $output .= drupal_render($form);
2163
21642
  return $output;
21650
}
2166
2167
/**
2168
 * Theme the theme select form.
2169
 * @param $form
2170
 *   An associative array containing the structure of the form.
2171
 * @ingroup themeable
2172
 */
217399
function theme_system_theme_select_form($form) {
21740
  foreach (element_children($form) as $key) {
21750
    $row = array();
21760
    if (isset($form[$key]['description']) &&
is_array($form[$key]['description'])) {
21770
      $row[] = drupal_render($form[$key]['screenshot']);
21780
      $row[] = drupal_render($form[$key]['description']);
21790
      $row[] = drupal_render($form['theme'][$key]);
21800
    }
21810
    $rows[] = $row;
21820
  }
2183
21840
  $header = array(t('Screenshot'), t('Name'), t('Selected'));
21850
  $output = theme('table', $header, $rows);
21860
  return $output;
21870
}
2188
2189
/**
2190
 * Theme function for the system themes form.
2191
 *
2192
 * @param $form
2193
 *   An associative array containing the structure of the form.
2194
 * @ingroup themeable
2195
 */
219699
function theme_system_themes_form($form) {
21972
  foreach (element_children($form) as $key) {
2198
    // Only look for themes
21992
    if (!isset($form[$key]['info'])) {
22002
      continue;
22010
    }
2202
2203
    // Fetch info
22042
    $info = $form[$key]['info']['#value'];
2205
    // Localize theme description.
22062
    $description = t($info['description']);
2207
    // Make sure it is compatible and render the checkbox if so.
22082
    if (isset($form['status']['#incompatible_themes_core'][$key])) {
22090
      unset($form['status'][$key]);
22100
      $status = theme('image', 'misc/watchdog-error.png',
t('incompatible'), t('Incompatible with this version of Drupal core'));
22110
      $description .= '<div class="incompatible">' . t('This version is
incompatible with the !core_version version of Drupal core.',
array('!core_version' => VERSION)) . '</div>';
22120
    }
22132
    elseif (isset($form['status']['#incompatible_themes_php'][$key])) {
22140
      unset($form['status'][$key]);
22150
      $status = theme('image', 'misc/watchdog-error.png',
t('incompatible'), t('Incompatible with this version of PHP'));
22160
      $php_required = $form['status']['#incompatible_themes_php'][$key];
22170
      if (substr_count($php_required, '.') < 2) {
22180
        $php_required .= '.*';
22190
      }
22200
      $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>';
22210
    }
2222
    else {
22232
      $status = drupal_render($form['status'][$key]);
2224
    }
2225
2226
    // Style theme info
22272
    $theme = '<div class="theme-info"><h2>' . $info['name'] . '</h2><div
class="description">' . $description . '</div></div>';
2228
2229
    // Build rows
22302
    $row = array();
22312
    $row[] = drupal_render($form[$key]['screenshot']);
22322
    $row[] = $theme;
22332
    $row[] = isset($info['version']) ? $info['version'] : '';
22342
    $row[] = array('data' => $status, 'align' => 'center');
22352
    if ($form['theme_default']) {
22362
      $row[] = array('data' => drupal_render($form['theme_default'][$key]),
'align' => 'center');
22372
      $row[] = array('data' => drupal_render($form[$key]['operations']),
'align' => 'center');
22382
    }
22392
    $rows[] = $row;
22402
  }
2241
22422
  $header = array(t('Screenshot'), t('Name'), t('Version'), t('Enabled'),
t('Default'), t('Operations'));
22432
  $output = theme('table', $header, $rows);
22442
  $output .= drupal_render($form);
22452
  return $output;
22460
}
224799