Project

General

Profile

Paste
Download (114 KB) Statistics
| Branch: | Revision:

root / drupal7 / modules / system / system.admin.inc @ b0dc3a2e

1
<?php
2

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

    
8
/**
9
 * Menu callback; Provide the administration overview page.
10
 */
11
function system_admin_config_page() {
12
  // Check for status report errors.
13
  if (system_status(TRUE) && user_access('administer site configuration')) {
14
    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');
15
  }
16
  $blocks = array();
17
  if ($admin = db_query("SELECT menu_name, mlid FROM {menu_links} WHERE link_path = 'admin/config' AND module = 'system'")->fetchAssoc()) {
18
    $result = db_query("
19
      SELECT m.*, ml.*
20
      FROM {menu_links} ml
21
      INNER JOIN {menu_router} m ON ml.router_path = m.path
22
      WHERE ml.link_path <> 'admin/help' AND menu_name = :menu_name AND ml.plid = :mlid AND hidden = 0", $admin, array('fetch' => PDO::FETCH_ASSOC));
23
    foreach ($result as $item) {
24
      _menu_link_translate($item);
25
      if (!$item['access']) {
26
        continue;
27
      }
28
      // The link description, either derived from 'description' in hook_menu()
29
      // or customized via menu module is used as title attribute.
30
      if (!empty($item['localized_options']['attributes']['title'])) {
31
        $item['description'] = $item['localized_options']['attributes']['title'];
32
        unset($item['localized_options']['attributes']['title']);
33
      }
34
      $block = $item;
35
      $block['content'] = '';
36
      $block['content'] .= theme('admin_block_content', array('content' => system_admin_menu_block($item)));
37
      if (!empty($block['content'])) {
38
        $block['show'] = TRUE;
39
      }
40

    
41
      // Prepare for sorting as in function _menu_tree_check_access().
42
      // The weight is offset so it is always positive, with a uniform 5-digits.
43
      $blocks[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' . $item['mlid']] = $block;
44
    }
45
  }
46
  if ($blocks) {
47
    ksort($blocks);
48
    return theme('admin_page', array('blocks' => $blocks));
49
  }
50
  else {
51
    return t('You do not have any administrative items.');
52
  }
53
}
54

    
55
/**
56
 * Provide a single block from the administration menu as a page.
57
 *
58
 * This function is often a destination for these blocks.
59
 * For example, 'admin/structure/types' needs to have a destination to be valid
60
 * in the Drupal menu system, but too much information there might be
61
 * hidden, so we supply the contents of the block.
62
 *
63
 * @return
64
 *   The output HTML.
65
 */
66
function system_admin_menu_block_page() {
67
  $item = menu_get_item();
68
  if ($content = system_admin_menu_block($item)) {
69
    $output = theme('admin_block_content', array('content' => $content));
70
  }
71
  else {
72
    $output = t('You do not have any administrative items.');
73
  }
74
  return $output;
75
}
76

    
77
/**
78
 * Menu callback; prints a listing of admin tasks, organized by module.
79
 */
80
function system_admin_index() {
81
  $module_info = system_get_info('module');
82
  foreach ($module_info as $module => $info) {
83
    $module_info[$module] = new stdClass();
84
    $module_info[$module]->info = $info;
85
  }
86
  uasort($module_info, 'system_sort_modules_by_info_name');
87
  $menu_items = array();
88

    
89
  foreach ($module_info as $module => $info) {
90
    // Only display a section if there are any available tasks.
91
    if ($admin_tasks = system_get_module_admin_tasks($module, $info->info)) {
92
      // Sort links by title.
93
      uasort($admin_tasks, 'drupal_sort_title');
94
      // Move 'Configure permissions' links to the bottom of each section.
95
      $permission_key = "admin/people/permissions#module-$module";
96
      if (isset($admin_tasks[$permission_key])) {
97
        $permission_task = $admin_tasks[$permission_key];
98
        unset($admin_tasks[$permission_key]);
99
        $admin_tasks[$permission_key] = $permission_task;
100
      }
101

    
102
      $menu_items[$info->info['name']] = array($info->info['description'], $admin_tasks);
103
    }
104
  }
105
  return theme('system_admin_index', array('menu_items' => $menu_items));
106
}
107

    
108
/**
109
 * Displays the configuration overview page.
110
 *
111
 * This menu callback implementation is a legacy function that used to display
112
 * the configuration overview page at admin/config. It is currently unused and
113
 * will be removed in Drupal 8. The page at admin/config is now generated by
114
 * system_admin_config_page().
115
 *
116
 * @deprecated
117
 * @see system_admin_config_page()
118
 */
119
function system_settings_overview() {
120
  // Check database setup if necessary
121
  if (function_exists('db_check_setup') && empty($_POST)) {
122
    db_check_setup();
123
  }
124

    
125
  $item = menu_get_item('admin/config');
126
  $content = system_admin_menu_block($item);
127

    
128
  $output = theme('admin_block_content', array('content' => $content));
129

    
130
  return $output;
131
}
132

    
133
/**
134
 * Menu callback; displays a listing of all themes.
135
 */
136
function system_themes_page() {
137
  // Get current list of themes.
138
  $themes = system_rebuild_theme_data();
139
  uasort($themes, 'system_sort_modules_by_info_name');
140

    
141
  $theme_default = variable_get('theme_default', 'bartik');
142
  $theme_groups  = array();
143

    
144
  foreach ($themes as &$theme) {
145
    if (!empty($theme->info['hidden'])) {
146
      continue;
147
    }
148
    $admin_theme_options[$theme->name] = $theme->info['name'];
149
    $theme->is_default = ($theme->name == $theme_default);
150

    
151
    // Identify theme screenshot.
152
    $theme->screenshot = NULL;
153
    // Create a list which includes the current theme and all its base themes.
154
    if (isset($themes[$theme->name]->base_themes)) {
155
      $theme_keys = array_keys($themes[$theme->name]->base_themes);
156
      $theme_keys[] = $theme->name;
157
    }
158
    else {
159
      $theme_keys = array($theme->name);
160
    }
161
    // Look for a screenshot in the current theme or in its closest ancestor.
162
    foreach (array_reverse($theme_keys) as $theme_key) {
163
      if (isset($themes[$theme_key]) && file_exists($themes[$theme_key]->info['screenshot'])) {
164
        $theme->screenshot = array(
165
          'path' => $themes[$theme_key]->info['screenshot'],
166
          'alt' => t('Screenshot for !theme theme', array('!theme' => $theme->info['name'])),
167
          'title' => t('Screenshot for !theme theme', array('!theme' => $theme->info['name'])),
168
          'attributes' => array('class' => array('screenshot')),
169
        );
170
        break;
171
      }
172
    }
173

    
174
    if (empty($theme->status)) {
175
     // Ensure this theme is compatible with this version of core.
176
     // Require the 'content' region to make sure the main page
177
     // content has a common place in all themes.
178
      $theme->incompatible_core = !isset($theme->info['core']) || ($theme->info['core'] != DRUPAL_CORE_COMPATIBILITY) || (!isset($theme->info['regions']['content']));
179
      $theme->incompatible_php = version_compare(phpversion(), $theme->info['php']) < 0;
180
    }
181
    $query['token'] = drupal_get_token('system-theme-operation-link');
182
    $theme->operations = array();
183
    if (!empty($theme->status) || !$theme->incompatible_core && !$theme->incompatible_php) {
184
      // Create the operations links.
185
      $query['theme'] = $theme->name;
186
      if (drupal_theme_access($theme)) {
187
        $theme->operations[] = array(
188
          'title' => t('Settings'),
189
          'href' => 'admin/appearance/settings/' . $theme->name,
190
          'attributes' => array('title' => t('Settings for !theme theme', array('!theme' => $theme->info['name']))),
191
        );
192
      }
193
      if (!empty($theme->status)) {
194
        if (!$theme->is_default) {
195
          $theme->operations[] = array(
196
            'title' => t('Disable'),
197
            'href' => 'admin/appearance/disable',
198
            'query' => $query,
199
            'attributes' => array('title' => t('Disable !theme theme', array('!theme' => $theme->info['name']))),
200
          );
201
          $theme->operations[] = array(
202
            'title' => t('Set default'),
203
            'href' => 'admin/appearance/default',
204
            'query' => $query,
205
            'attributes' => array('title' => t('Set !theme as default theme', array('!theme' => $theme->info['name']))),
206
          );
207
        }
208
      }
209
      else {
210
        $theme->operations[] = array(
211
          'title' => t('Enable'),
212
          'href' => 'admin/appearance/enable',
213
          'query' => $query,
214
          'attributes' => array('title' => t('Enable !theme theme', array('!theme' => $theme->info['name']))),
215
        );
216
        $theme->operations[] = array(
217
          'title' => t('Enable and set default'),
218
          'href' => 'admin/appearance/default',
219
          'query' => $query,
220
          'attributes' => array('title' => t('Enable !theme as default theme', array('!theme' => $theme->info['name']))),
221
        );
222
      }
223
    }
224

    
225
    // Add notes to default and administration theme.
226
    $theme->notes = array();
227
    $theme->classes = array();
228
    if ($theme->is_default) {
229
      $theme->classes[] = 'theme-default';
230
      $theme->notes[] = t('default theme');
231
    }
232

    
233
    // Sort enabled and disabled themes into their own groups.
234
    $theme_groups[$theme->status ? 'enabled' : 'disabled'][] = $theme;
235
  }
236

    
237
  // There are two possible theme groups.
238
  $theme_group_titles = array(
239
    'enabled' => format_plural(count($theme_groups['enabled']), 'Enabled theme', 'Enabled themes'),
240
  );
241
  if (!empty($theme_groups['disabled'])) {
242
    $theme_group_titles['disabled'] = format_plural(count($theme_groups['disabled']), 'Disabled theme', 'Disabled themes');
243
  }
244

    
245
  uasort($theme_groups['enabled'], 'system_sort_themes');
246
  drupal_alter('system_themes_page', $theme_groups);
247

    
248
  $admin_form = drupal_get_form('system_themes_admin_form', $admin_theme_options);
249
  return theme('system_themes_page', array('theme_groups' => $theme_groups, 'theme_group_titles' => $theme_group_titles)) . drupal_render($admin_form);
250
}
251

    
252
/**
253
 * Form to select the administration theme.
254
 *
255
 * @ingroup forms
256
 * @see system_themes_admin_form_submit()
257
 */
258
function system_themes_admin_form($form, &$form_state, $theme_options) {
259
  // Administration theme settings.
260
  $form['admin_theme'] = array(
261
    '#type' => 'fieldset',
262
    '#title' => t('Administration theme'),
263
  );
264
  $form['admin_theme']['admin_theme'] = array(
265
    '#type' => 'select',
266
    '#options' => array(0 => t('Default theme')) + $theme_options,
267
    '#title' => t('Administration theme'),
268
    '#description' => t('Choose "Default theme" to always use the same theme as the rest of the site.'),
269
    '#default_value' => variable_get('admin_theme', 0),
270
  );
271
  $form['admin_theme']['node_admin_theme'] = array(
272
    '#type' => 'checkbox',
273
    '#title' => t('Use the administration theme when editing or creating content'),
274
    '#default_value' => variable_get('node_admin_theme', '0'),
275
  );
276
  $form['admin_theme']['actions'] = array('#type' => 'actions');
277
  $form['admin_theme']['actions']['submit'] = array(
278
    '#type' => 'submit',
279
    '#value' => t('Save configuration'),
280
  );
281
  return $form;
282
}
283

    
284
/**
285
 * Process system_themes_admin_form form submissions.
286
 */
287
function system_themes_admin_form_submit($form, &$form_state) {
288
  drupal_set_message(t('The configuration options have been saved.'));
289
  variable_set('admin_theme', $form_state['values']['admin_theme']);
290
  variable_set('node_admin_theme', $form_state['values']['node_admin_theme']);
291
}
292

    
293
/**
294
 * Menu callback; Enables a theme.
295
 */
296
function system_theme_enable() {
297
  if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) {
298
    $theme = $_REQUEST['theme'];
299
    // Get current list of themes.
300
    $themes = list_themes();
301

    
302
    // Check if the specified theme is one recognized by the system.
303
    if (!empty($themes[$theme])) {
304
      theme_enable(array($theme));
305
      drupal_set_message(t('The %theme theme has been enabled.', array('%theme' => $themes[$theme]->info['name'])));
306
    }
307
    else {
308
      drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
309
    }
310
    drupal_goto('admin/appearance');
311
  }
312
  return MENU_ACCESS_DENIED;
313
}
314

    
315
/**
316
 * Menu callback; Disables a theme.
317
 */
318
function system_theme_disable() {
319
  if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) {
320
    $theme = $_REQUEST['theme'];
321
    // Get current list of themes.
322
    $themes = list_themes();
323

    
324
    // Check if the specified theme is one recognized by the system.
325
    if (!empty($themes[$theme])) {
326
      if ($theme == variable_get('theme_default', 'bartik')) {
327
        // Don't disable the default theme.
328
        drupal_set_message(t('%theme is the default theme and cannot be disabled.', array('%theme' => $themes[$theme]->info['name'])), 'error');
329
      }
330
      else {
331
        theme_disable(array($theme));
332
        drupal_set_message(t('The %theme theme has been disabled.', array('%theme' => $themes[$theme]->info['name'])));
333
      }
334
    }
335
    else {
336
      drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
337
    }
338
    drupal_goto('admin/appearance');
339
  }
340
  return MENU_ACCESS_DENIED;
341
}
342

    
343
/**
344
 * Menu callback; Set the default theme.
345
 */
346
function system_theme_default() {
347
  if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) {
348
    $theme = $_REQUEST['theme'];
349
    // Get current list of themes.
350
    $themes = list_themes();
351

    
352
    // Check if the specified theme is one recognized by the system.
353
    if (!empty($themes[$theme])) {
354
      // Enable the theme if it is currently disabled.
355
      if (empty($themes[$theme]->status)) {
356
       theme_enable(array($theme));
357
      }
358
      // Set the default theme.
359
      variable_set('theme_default', $theme);
360

    
361
      // Rebuild the menu. This duplicates the menu_rebuild() in theme_enable().
362
      // However, modules must know the current default theme in order to use
363
      // this information in hook_menu() or hook_menu_alter() implementations,
364
      // and doing the variable_set() before the theme_enable() could result
365
      // in a race condition where the theme is default but not enabled.
366
      menu_rebuild();
367

    
368
      // The status message depends on whether an admin theme is currently in use:
369
      // a value of 0 means the admin theme is set to be the default theme.
370
      $admin_theme = variable_get('admin_theme', 0);
371
      if ($admin_theme != 0 && $admin_theme != $theme) {
372
        drupal_set_message(t('Please note that the administration theme 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(
373
          '%admin_theme' => $themes[$admin_theme]->info['name'],
374
          '%selected_theme' => $themes[$theme]->info['name'],
375
        )));
376
      }
377
      else {
378
        drupal_set_message(t('%theme is now the default theme.', array('%theme' => $themes[$theme]->info['name'])));
379
      }
380
    }
381
    else {
382
      drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
383
    }
384
    drupal_goto('admin/appearance');
385
  }
386
  return MENU_ACCESS_DENIED;
387
}
388

    
389
/**
390
 * Form builder; display theme configuration for entire site and individual themes.
391
 *
392
 * @param $key
393
 *   A theme name.
394
 * @return
395
 *   The form structure.
396
 * @ingroup forms
397
 * @see system_theme_settings_submit()
398
 */
399
function system_theme_settings($form, &$form_state, $key = '') {
400
  // Default settings are defined in theme_get_setting() in includes/theme.inc
401
  if ($key) {
402
    $var = 'theme_' . $key . '_settings';
403
    $themes = list_themes();
404
    $features = $themes[$key]->info['features'];
405
  }
406
  else {
407
    $var = 'theme_settings';
408
  }
409

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

    
412
  // Toggle settings
413
  $toggles = array(
414
    'logo'                      => t('Logo'),
415
    'name'                      => t('Site name'),
416
    'slogan'                    => t('Site slogan'),
417
    'node_user_picture'         => t('User pictures in posts'),
418
    'comment_user_picture'      => t('User pictures in comments'),
419
    'comment_user_verification' => t('User verification status in comments'),
420
    'favicon'                   => t('Shortcut icon'),
421
    'main_menu'                 => t('Main menu'),
422
    'secondary_menu'            => t('Secondary menu'),
423
  );
424

    
425
  // Some features are not always available
426
  $disabled = array();
427
  if (!variable_get('user_pictures', 0)) {
428
    $disabled['toggle_node_user_picture'] = TRUE;
429
    $disabled['toggle_comment_user_picture'] = TRUE;
430
  }
431
  if (!module_exists('comment')) {
432
    $disabled['toggle_comment_user_picture'] = TRUE;
433
    $disabled['toggle_comment_user_verification'] = TRUE;
434
  }
435

    
436
  $form['theme_settings'] = array(
437
    '#type' => 'fieldset',
438
    '#title' => t('Toggle display'),
439
    '#description' => t('Enable or disable the display of certain page elements.'),
440
  );
441
  foreach ($toggles as $name => $title) {
442
    if ((!$key) || in_array($name, $features)) {
443
      $form['theme_settings']['toggle_' . $name] = array('#type' => 'checkbox', '#title' => $title, '#default_value' => theme_get_setting('toggle_' . $name, $key));
444
      // Disable checkboxes for features not supported in the current configuration.
445
      if (isset($disabled['toggle_' . $name])) {
446
        $form['theme_settings']['toggle_' . $name]['#disabled'] = TRUE;
447
      }
448
    }
449
  }
450

    
451
  if (!element_children($form['theme_settings'])) {
452
    // If there is no element in the theme settings fieldset then do not show
453
    // it -- but keep it in the form if another module wants to alter.
454
    $form['theme_settings']['#access'] = FALSE;
455
  }
456

    
457
  // Logo settings
458
  if ((!$key) || in_array('logo', $features)) {
459
    $form['logo'] = array(
460
      '#type' => 'fieldset',
461
      '#title' => t('Logo image settings'),
462
      '#description' => t('If toggled on, the following logo will be displayed.'),
463
      '#attributes' => array('class' => array('theme-settings-bottom')),
464
    );
465
    $form['logo']['default_logo'] = array(
466
      '#type' => 'checkbox',
467
      '#title' => t('Use the default logo'),
468
      '#default_value' => theme_get_setting('default_logo', $key),
469
      '#tree' => FALSE,
470
      '#description' => t('Check here if you want the theme to use the logo supplied with it.')
471
    );
472
    $form['logo']['settings'] = array(
473
      '#type' => 'container',
474
      '#states' => array(
475
        // Hide the logo settings when using the default logo.
476
        'invisible' => array(
477
          'input[name="default_logo"]' => array('checked' => TRUE),
478
        ),
479
      ),
480
    );
481
    $form['logo']['settings']['logo_path'] = array(
482
      '#type' => 'textfield',
483
      '#title' => t('Path to custom logo'),
484
      '#description' => t('The path to the file you would like to use as your logo file instead of the default logo.'),
485
      '#default_value' => theme_get_setting('logo_path', $key),
486
    );
487
    $form['logo']['settings']['logo_upload'] = array(
488
      '#type' => 'file',
489
      '#title' => t('Upload logo image'),
490
      '#maxlength' => 40,
491
      '#description' => t("If you don't have direct file access to the server, use this field to upload your logo.")
492
    );
493
  }
494

    
495
  if ((!$key) || in_array('favicon', $features)) {
496
    $form['favicon'] = array(
497
      '#type' => 'fieldset',
498
      '#title' => t('Shortcut icon settings'),
499
      '#description' => t("Your shortcut icon, or 'favicon', is displayed in the address bar and bookmarks of most browsers."),
500
    );
501
    $form['favicon']['default_favicon'] = array(
502
      '#type' => 'checkbox',
503
      '#title' => t('Use the default shortcut icon.'),
504
      '#default_value' => theme_get_setting('default_favicon', $key),
505
      '#description' => t('Check here if you want the theme to use the default shortcut icon.')
506
    );
507
    $form['favicon']['settings'] = array(
508
      '#type' => 'container',
509
      '#states' => array(
510
        // Hide the favicon settings when using the default favicon.
511
        'invisible' => array(
512
          'input[name="default_favicon"]' => array('checked' => TRUE),
513
        ),
514
      ),
515
    );
516
    $form['favicon']['settings']['favicon_path'] = array(
517
      '#type' => 'textfield',
518
      '#title' => t('Path to custom icon'),
519
      '#description' => t('The path to the image file you would like to use as your custom shortcut icon.'),
520
      '#default_value' => theme_get_setting('favicon_path', $key),
521
    );
522
    $form['favicon']['settings']['favicon_upload'] = array(
523
      '#type' => 'file',
524
      '#title' => t('Upload icon image'),
525
      '#description' => t("If you don't have direct file access to the server, use this field to upload your shortcut icon.")
526
    );
527
  }
528

    
529
  // Inject human-friendly values for logo and favicon.
530
  foreach (array('logo' => 'logo.png', 'favicon' => 'favicon.ico') as $type => $default) {
531
    if (isset($form[$type]['settings'][$type . '_path'])) {
532
      $element = &$form[$type]['settings'][$type . '_path'];
533

    
534
      // If path is a public:// URI, display the path relative to the files
535
      // directory; stream wrappers are not end-user friendly.
536
      $original_path = $element['#default_value'];
537
      $friendly_path = NULL;
538
      if (file_uri_scheme($original_path) == 'public') {
539
        $friendly_path = file_uri_target($original_path);
540
        $element['#default_value'] = $friendly_path;
541
      }
542
    }
543
  }
544

    
545
  if ($key) {
546
    // Call engine-specific settings.
547
    $function = $themes[$key]->prefix . '_engine_settings';
548
    if (function_exists($function)) {
549
      $form['engine_specific'] = array(
550
        '#type' => 'fieldset',
551
        '#title' => t('Theme-engine-specific settings'),
552
        '#description' => t('These settings only exist for the themes based on the %engine theme engine.', array('%engine' => $themes[$key]->prefix)),
553
      );
554
      $function($form, $form_state);
555
    }
556

    
557
    // Create a list which includes the current theme and all its base themes.
558
    if (isset($themes[$key]->base_themes)) {
559
      $theme_keys = array_keys($themes[$key]->base_themes);
560
      $theme_keys[] = $key;
561
    }
562
    else {
563
      $theme_keys = array($key);
564
    }
565

    
566
    // Save the name of the current theme (if any), so that we can temporarily
567
    // override the current theme and allow theme_get_setting() to work
568
    // without having to pass the theme name to it.
569
    $default_theme = !empty($GLOBALS['theme_key']) ? $GLOBALS['theme_key'] : NULL;
570
    $GLOBALS['theme_key'] = $key;
571

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

    
580
      // Call theme-specific settings.
581
      $function = $theme . '_form_system_theme_settings_alter';
582
      if (function_exists($function)) {
583
        $function($form, $form_state);
584
      }
585
    }
586

    
587
    // Restore the original current theme.
588
    if (isset($default_theme)) {
589
      $GLOBALS['theme_key'] = $default_theme;
590
    }
591
    else {
592
      unset($GLOBALS['theme_key']);
593
    }
594
  }
595

    
596
  $form = system_settings_form($form);
597
  // We don't want to call system_settings_form_submit(), so change #submit.
598
  array_pop($form['#submit']);
599
  $form['#submit'][] = 'system_theme_settings_submit';
600
  $form['#validate'][] = 'system_theme_settings_validate';
601
  return $form;
602
}
603

    
604
/**
605
 * Validator for the system_theme_settings() form.
606
 */
607
function system_theme_settings_validate($form, &$form_state) {
608
  // Handle file uploads.
609
  $validators = array('file_validate_is_image' => array());
610

    
611
  // Check for a new uploaded logo.
612
  $file = file_save_upload('logo_upload', $validators);
613
  if (isset($file)) {
614
    // File upload was attempted.
615
    if ($file) {
616
      // Put the temporary file in form_values so we can save it on submit.
617
      $form_state['values']['logo_upload'] = $file;
618
    }
619
    else {
620
      // File upload failed.
621
      form_set_error('logo_upload', t('The logo could not be uploaded.'));
622
    }
623
  }
624

    
625
  $validators = array('file_validate_extensions' => array('ico png gif jpg jpeg apng svg'));
626

    
627
  // Check for a new uploaded favicon.
628
  $file = file_save_upload('favicon_upload', $validators);
629
  if (isset($file)) {
630
    // File upload was attempted.
631
    if ($file) {
632
      // Put the temporary file in form_values so we can save it on submit.
633
      $form_state['values']['favicon_upload'] = $file;
634
    }
635
    else {
636
      // File upload failed.
637
      form_set_error('favicon_upload', t('The favicon could not be uploaded.'));
638
    }
639
  }
640

    
641
  // If the user provided a path for a logo or favicon file, make sure a file
642
  // exists at that path.
643
  if (!empty($form_state['values']['logo_path'])) {
644
    $path = _system_theme_settings_validate_path($form_state['values']['logo_path']);
645
    if (!$path) {
646
      form_set_error('logo_path', t('The custom logo path is invalid.'));
647
    }
648
  }
649
  if (!empty($form_state['values']['favicon_path'])) {
650
    $path = _system_theme_settings_validate_path($form_state['values']['favicon_path']);
651
    if (!$path) {
652
      form_set_error('favicon_path', t('The custom favicon path is invalid.'));
653
    }
654
  }
655
}
656

    
657
/**
658
 * Helper function for the system_theme_settings form.
659
 *
660
 * Attempts to validate normal system paths, paths relative to the public files
661
 * directory, or stream wrapper URIs. If the given path is any of the above,
662
 * returns a valid path or URI that the theme system can display.
663
 *
664
 * @param $path
665
 *   A path relative to the Drupal root or to the public files directory, or
666
 *   a stream wrapper URI.
667
 * @return mixed
668
 *   A valid path that can be displayed through the theme system, or FALSE if
669
 *   the path could not be validated.
670
 */
671
function _system_theme_settings_validate_path($path) {
672
  // Absolute local file paths are invalid.
673
  if (drupal_realpath($path) == $path) {
674
    return FALSE;
675
  }
676
  // A path relative to the Drupal root or a fully qualified URI is valid.
677
  if (is_file($path)) {
678
    return $path;
679
  }
680
  // Prepend 'public://' for relative file paths within public filesystem.
681
  if (file_uri_scheme($path) === FALSE) {
682
    $path = 'public://' . $path;
683
  }
684
  if (is_file($path)) {
685
    return $path;
686
  }
687
  return FALSE;
688
}
689

    
690
/**
691
 * Process system_theme_settings form submissions.
692
 */
693
function system_theme_settings_submit($form, &$form_state) {
694
  // Exclude unnecessary elements before saving.
695
  form_state_values_clean($form_state);
696

    
697
  $values = $form_state['values'];
698

    
699
  // Extract the name of the theme from the submitted form values, then remove
700
  // it from the array so that it is not saved as part of the variable.
701
  $key = $values['var'];
702
  unset($values['var']);
703

    
704
  // If the user uploaded a new logo or favicon, save it to a permanent location
705
  // and use it in place of the default theme-provided file.
706
  if (!empty($values['logo_upload'])) {
707
    $file = $values['logo_upload'];
708
    unset($values['logo_upload']);
709
    $filename = file_unmanaged_copy($file->uri);
710
    $values['default_logo'] = 0;
711
    $values['logo_path'] = $filename;
712
    $values['toggle_logo'] = 1;
713
  }
714
  if (!empty($values['favicon_upload'])) {
715
    $file = $values['favicon_upload'];
716
    unset($values['favicon_upload']);
717
    $filename = file_unmanaged_copy($file->uri);
718
    $values['default_favicon'] = 0;
719
    $values['favicon_path'] = $filename;
720
    $values['toggle_favicon'] = 1;
721
  }
722

    
723
  // If the user entered a path relative to the system files directory for
724
  // a logo or favicon, store a public:// URI so the theme system can handle it.
725
  if (!empty($values['logo_path'])) {
726
    $values['logo_path'] = _system_theme_settings_validate_path($values['logo_path']);
727
  }
728
  if (!empty($values['favicon_path'])) {
729
    $values['favicon_path'] = _system_theme_settings_validate_path($values['favicon_path']);
730
  }
731

    
732
  if (empty($values['default_favicon']) && !empty($values['favicon_path'])) {
733
    $values['favicon_mimetype'] = file_get_mimetype($values['favicon_path']);
734
  }
735

    
736
  variable_set($key, $values);
737
  drupal_set_message(t('The configuration options have been saved.'));
738

    
739
  cache_clear_all();
740
}
741

    
742
/**
743
 * Recursively check compatibility.
744
 *
745
 * @param $incompatible
746
 *   An associative array which at the end of the check contains all
747
 *   incompatible files as the keys, their values being TRUE.
748
 * @param $files
749
 *   The set of files that will be tested.
750
 * @param $file
751
 *   The file at which the check starts.
752
 * @return
753
 *   Returns TRUE if an incompatible file is found, NULL (no return value)
754
 *   otherwise.
755
 */
756
function _system_is_incompatible(&$incompatible, $files, $file) {
757
  if (isset($incompatible[$file->name])) {
758
    return TRUE;
759
  }
760
  // Recursively traverse required modules, looking for incompatible modules.
761
  foreach ($file->requires as $requires) {
762
    if (isset($files[$requires]) && _system_is_incompatible($incompatible, $files, $files[$requires])) {
763
      $incompatible[$file->name] = TRUE;
764
      return TRUE;
765
    }
766
  }
767
}
768

    
769
/**
770
 * Menu callback; provides module enable/disable interface.
771
 *
772
 * The list of modules gets populated by module.info files, which contain each
773
 * module's name, description, and information about which modules it requires.
774
 * See drupal_parse_info_file() for information on module.info descriptors.
775
 *
776
 * Dependency checking is performed to ensure that a module:
777
 * - can not be enabled if there are disabled modules it requires.
778
 * - can not be disabled if there are enabled modules which depend on it.
779
 *
780
 * @param $form_state
781
 *   An associative array containing the current state of the form.
782
 *
783
 * @return
784
 *   The form array.
785
 *
786
 * @ingroup forms
787
 * @see theme_system_modules()
788
 * @see system_modules_submit()
789
 */
790
function system_modules($form, $form_state = array()) {
791
  // Get current list of modules.
792
  $files = system_rebuild_module_data();
793

    
794
  // Remove hidden modules from display list.
795
  $visible_files = $files;
796
  foreach ($visible_files as $filename => $file) {
797
    if (!empty($file->info['hidden'])) {
798
      unset($visible_files[$filename]);
799
    }
800
  }
801

    
802
  uasort($visible_files, 'system_sort_modules_by_info_name');
803

    
804
  // If the modules form was submitted, then system_modules_submit() runs first
805
  // and if there are unfilled required modules, then $form_state['storage'] is
806
  // filled, triggering a rebuild. In this case we need to display a
807
  // confirmation form.
808
  if (!empty($form_state['storage'])) {
809
    return system_modules_confirm_form($visible_files, $form_state['storage']);
810
  }
811

    
812
  $modules = array();
813
  $form['modules'] = array('#tree' => TRUE);
814

    
815
  // Used when checking if module implements a help page.
816
  $help_arg = module_exists('help') ? drupal_help_arg() : FALSE;
817

    
818
  // Used when displaying modules that are required by the installation profile.
819
  require_once DRUPAL_ROOT . '/includes/install.inc';
820
  $distribution_name = check_plain(drupal_install_profile_distribution_name());
821

    
822
  // Iterate through each of the modules.
823
  foreach ($visible_files as $filename => $module) {
824
    $extra = array();
825
    $extra['enabled'] = (bool) $module->status;
826
    if (!empty($module->info['required'] )) {
827
      $extra['disabled'] = TRUE;
828
      $extra['required_by'][] = $distribution_name . (!empty($module->info['explanation']) ? ' ('. $module->info['explanation'] .')' : '');
829
    }
830

    
831
    // If this module requires other modules, add them to the array.
832
    foreach ($module->requires as $requires => $v) {
833
      if (!isset($files[$requires])) {
834
        $extra['requires'][$requires] = t('@module (<span class="admin-missing">missing</span>)', array('@module' => drupal_ucfirst($requires)));
835
        $extra['disabled'] = TRUE;
836
      }
837
      // Only display visible modules.
838
      elseif (isset($visible_files[$requires])) {
839
        $requires_name = $files[$requires]->info['name'];
840
        // Disable this module if it is incompatible with the dependency's version.
841
        if ($incompatible_version = drupal_check_incompatibility($v, str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $files[$requires]->info['version']))) {
842
          $extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> version @version)', array(
843
            '@module' => $requires_name . $incompatible_version,
844
            '@version' => $files[$requires]->info['version'],
845
          ));
846
          $extra['disabled'] = TRUE;
847
        }
848
        // Disable this module if the dependency is incompatible with this
849
        // version of Drupal core.
850
        elseif ($files[$requires]->info['core'] != DRUPAL_CORE_COMPATIBILITY) {
851
          $extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> this version of Drupal core)', array(
852
            '@module' => $requires_name,
853
          ));
854
          $extra['disabled'] = TRUE;
855
        }
856
        elseif ($files[$requires]->status) {
857
          $extra['requires'][$requires] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $requires_name));
858
        }
859
        else {
860
          $extra['requires'][$requires] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $requires_name));
861
        }
862
      }
863
    }
864
    // Generate link for module's help page, if there is one.
865
    if ($help_arg && $module->status && in_array($filename, module_implements('help'))) {
866
      if (module_invoke($filename, 'help', "admin/help#$filename", $help_arg)) {
867
        $extra['links']['help'] = array(
868
          '#type' => 'link',
869
          '#title' => t('Help'),
870
          '#href' => "admin/help/$filename",
871
          '#options' => array('attributes' => array('class' =>  array('module-link', 'module-link-help'), 'title' => t('Help'))),
872
        );
873
      }
874
    }
875
    // Generate link for module's permission, if the user has access to it.
876
    if ($module->status && user_access('administer permissions') && in_array($filename, module_implements('permission'))) {
877
      $extra['links']['permissions'] = array(
878
        '#type' => 'link',
879
        '#title' => t('Permissions'),
880
        '#href' => 'admin/people/permissions',
881
        '#options' => array('fragment' => 'module-' . $filename, 'attributes' => array('class' => array('module-link', 'module-link-permissions'), 'title' => t('Configure permissions'))),
882
      );
883
    }
884
    // Generate link for module's configuration page, if the module provides
885
    // one.
886
    if ($module->status && isset($module->info['configure'])) {
887
      $configure_link = menu_get_item($module->info['configure']);
888
      if ($configure_link['access']) {
889
        $extra['links']['configure'] = array(
890
          '#type' => 'link',
891
          '#title' => t('Configure'),
892
          '#href' => $configure_link['href'],
893
          '#options' => array('attributes' => array('class' => array('module-link', 'module-link-configure'), 'title' => $configure_link['description'])),
894
        );
895
      }
896
    }
897

    
898
    // If this module is required by other modules, list those, and then make it
899
    // impossible to disable this one.
900
    foreach ($module->required_by as $required_by => $v) {
901
      // Hidden modules are unset already.
902
      if (isset($visible_files[$required_by])) {
903
        if ($files[$required_by]->status == 1 && $module->status == 1) {
904
          $extra['required_by'][] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $files[$required_by]->info['name']));
905
          $extra['disabled'] = TRUE;
906
        }
907
        else {
908
          $extra['required_by'][] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $files[$required_by]->info['name']));
909
        }
910
      }
911
    }
912
    $form['modules'][$module->info['package']][$filename] = _system_modules_build_row($module->info, $extra);
913
  }
914

    
915
  // Add basic information to the fieldsets.
916
  foreach (element_children($form['modules']) as $package) {
917
    $form['modules'][$package] += array(
918
      '#type' => 'fieldset',
919
      '#title' => t($package),
920
      '#collapsible' => TRUE,
921
      '#theme' => 'system_modules_fieldset',
922
      '#header' => array(
923
        array('data' => t('Enabled'), 'class' => array('checkbox')),
924
        t('Name'),
925
        t('Version'),
926
        t('Description'),
927
        array('data' => t('Operations'), 'colspan' => 3),
928
      ),
929
      // Ensure that the "Core" package fieldset comes first.
930
      '#weight' => $package == 'Core' ? -10 : NULL,
931
    );
932
  }
933

    
934
  // Lastly, sort all fieldsets by title.
935
  uasort($form['modules'], 'element_sort_by_title');
936

    
937
  $form['actions'] = array('#type' => 'actions');
938
  $form['actions']['submit'] = array(
939
    '#type' => 'submit',
940
    '#value' => t('Save configuration'),
941
  );
942
  $form['#action'] = url('admin/modules/list/confirm');
943

    
944
  return $form;
945
}
946

    
947
/**
948
 * Array sorting callback; sorts modules or themes by their name.
949
 */
950
function system_sort_modules_by_info_name($a, $b) {
951
  return strcasecmp($a->info['name'], $b->info['name']);
952
}
953

    
954
/**
955
 * Sorts themes by their names, with the default theme listed first.
956
 *
957
 * Callback for uasort() within system_themes_page().
958
 *
959
 * @see system_sort_modules_by_info_name().
960
 */
961
function system_sort_themes($a, $b) {
962
  if ($a->is_default) {
963
    return -1;
964
  }
965
  if ($b->is_default) {
966
    return 1;
967
  }
968
  return strcasecmp($a->info['name'], $b->info['name']);
969
}
970

    
971
/**
972
 * Build a table row for the system modules page.
973
 */
974
function _system_modules_build_row($info, $extra) {
975
  // Add in the defaults.
976
  $extra += array(
977
    'requires' => array(),
978
    'required_by' => array(),
979
    'disabled' => FALSE,
980
    'enabled' => FALSE,
981
    'links' => array(),
982
  );
983
  $form = array(
984
    '#tree' => TRUE,
985
  );
986
  // Set the basic properties.
987
  $form['name'] = array(
988
    '#markup' => $info['name'],
989
  );
990
  $form['description'] = array(
991
    '#markup' => t($info['description']),
992
  );
993
  $form['version'] = array(
994
    '#markup' => $info['version'],
995
  );
996
  $form['#requires'] = $extra['requires'];
997
  $form['#required_by'] = $extra['required_by'];
998

    
999
  // Check the compatibilities.
1000
  $compatible = TRUE;
1001
  $status_short = '';
1002
  $status_long = '';
1003

    
1004
  // Initialize empty arrays of long and short reasons explaining why the
1005
  // module is incompatible.
1006
  // Add each reason as a separate element in both the arrays.
1007
  $reasons_short = array();
1008
  $reasons_long = array();
1009

    
1010
  // Check the core compatibility.
1011
  if (!isset($info['core']) || $info['core'] != DRUPAL_CORE_COMPATIBILITY) {
1012
    $compatible = FALSE;
1013
    $reasons_short[] = t('Incompatible with this version of Drupal core.');
1014
    $reasons_long[] = t('This version is not compatible with Drupal !core_version and should be replaced.', array('!core_version' => DRUPAL_CORE_COMPATIBILITY));
1015
  }
1016

    
1017
  // Ensure this module is compatible with the currently installed version of PHP.
1018
  if (version_compare(phpversion(), $info['php']) < 0) {
1019
    $compatible = FALSE;
1020
    $reasons_short[] = t('Incompatible with this version of PHP');
1021
    $php_required = $info['php'];
1022
    if (substr_count($info['php'], '.') < 2) {
1023
      $php_required .= '.*';
1024
    }
1025
    $reasons_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()));
1026
  }
1027

    
1028
  // If this module is compatible, present a checkbox indicating
1029
  // this module may be installed. Otherwise, show a big red X.
1030
  if ($compatible) {
1031
    $form['enable'] = array(
1032
      '#type' => 'checkbox',
1033
      '#title' => t('Enable'),
1034
      '#default_value' => $extra['enabled'],
1035
    );
1036
    if ($extra['disabled']) {
1037
      $form['enable']['#disabled'] = TRUE;
1038
    }
1039
  }
1040
  else {
1041
    $status_short = implode(' ', $reasons_short);
1042
    $status_long = implode(' ', $reasons_long);
1043
    $form['enable'] = array(
1044
      '#markup' =>  theme('image', array('path' => 'misc/watchdog-error.png', 'alt' => $status_short, 'title' => $status_short)),
1045
    );
1046
    $form['description']['#markup'] .= theme('system_modules_incompatible', array('message' => $status_long));
1047
  }
1048

    
1049
  // Build operation links.
1050
  foreach (array('help', 'permissions', 'configure') as $key) {
1051
    $form['links'][$key] = (isset($extra['links'][$key]) ? $extra['links'][$key] : array());
1052
  }
1053

    
1054
  return $form;
1055
}
1056

    
1057
/**
1058
 * Display confirmation form for required modules.
1059
 *
1060
 * @param $modules
1061
 *   Array of module file objects as returned from system_rebuild_module_data().
1062
 * @param $storage
1063
 *   The contents of $form_state['storage']; an array with two
1064
 *   elements: the list of required modules and the list of status
1065
 *   form field values from the previous screen.
1066
 * @ingroup forms
1067
 */
1068
function system_modules_confirm_form($modules, $storage) {
1069
  $items = array();
1070

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

    
1074
  foreach ($storage['more_required'] as $info) {
1075
    $t_argument = array(
1076
      '@module' => $info['name'],
1077
      '@required' => implode(', ', $info['requires']),
1078
    );
1079
    $items[] = format_plural(count($info['requires']), 'You must enable the @required module to install @module.', 'You must enable the @required modules to install @module.', $t_argument);
1080
  }
1081

    
1082
  foreach ($storage['missing_modules'] as $name => $info) {
1083
    $t_argument = array(
1084
      '@module' => $name,
1085
      '@depends' => implode(', ', $info['depends']),
1086
    );
1087
    $items[] = format_plural(count($info['depends']), 'The @module module is missing, so the following module will be disabled: @depends.', 'The @module module is missing, so the following modules will be disabled: @depends.', $t_argument);
1088
  }
1089

    
1090
  $form['text'] = array('#markup' => theme('item_list', array('items' => $items)));
1091

    
1092
  if ($form) {
1093
    // Set some default form values
1094
    $form = confirm_form(
1095
      $form,
1096
      t('Some required modules must be enabled'),
1097
      'admin/modules',
1098
      t('Would you like to continue with the above?'),
1099
      t('Continue'),
1100
      t('Cancel'));
1101
    return $form;
1102
  }
1103
}
1104

    
1105
/**
1106
 * Submit callback; handles modules form submission.
1107
 */
1108
function system_modules_submit($form, &$form_state) {
1109
  include_once DRUPAL_ROOT . '/includes/install.inc';
1110

    
1111
  // Builds list of modules.
1112
  $modules = array();
1113
  // If we're not coming from the confirmation form, build the list of modules.
1114
  if (empty($form_state['storage'])) {
1115
    // If we're not coming from the confirmation form, build the module list.
1116
    foreach ($form_state['values']['modules'] as $group_name => $group) {
1117
      foreach ($group as $module => $enabled) {
1118
        $modules[$module] = array('group' => $group_name, 'enabled' => $enabled['enable']);
1119
      }
1120
    }
1121
  }
1122
  else {
1123
    // If we are coming from the confirmation form, fetch
1124
    // the modules out of $form_state.
1125
    $modules = $form_state['storage']['modules'];
1126
  }
1127

    
1128
  // Collect data for all modules to be able to determine dependencies.
1129
  $files = system_rebuild_module_data();
1130

    
1131
  // Sorts modules by weight.
1132
  $sort = array();
1133
  foreach (array_keys($modules) as $module) {
1134
    $sort[$module] = $files[$module]->sort;
1135
  }
1136
  array_multisort($sort, $modules);
1137

    
1138
  // Makes sure all required modules are set to be enabled.
1139
  $more_required = array();
1140
  $missing_modules = array();
1141
  foreach ($modules as $name => $module) {
1142
    if ($module['enabled']) {
1143
      // Checks that all dependencies are set to be enabled.  Stores the ones
1144
      // that are not in $dependencies variable so that the user can be alerted
1145
      // in the confirmation form that more modules need to be enabled.
1146
      $dependencies = array();
1147
      foreach (array_keys($files[$name]->requires) as $required) {
1148
        if (empty($modules[$required]['enabled'])) {
1149
          if (isset($files[$required])) {
1150
            $dependencies[] = $files[$required]->info['name'];
1151
            $modules[$required]['enabled'] = TRUE;
1152
          }
1153
          else {
1154
            $missing_modules[$required]['depends'][] = $name;
1155
            $modules[$name]['enabled'] = FALSE;
1156
          }
1157
        }
1158
      }
1159

    
1160
      // Stores additional modules that need to be enabled in $more_required.
1161
      if (!empty($dependencies)) {
1162
        $more_required[$name] = array(
1163
          'name' => $files[$name]->info['name'],
1164
          'requires' => $dependencies,
1165
        );
1166
      }
1167
    }
1168
  }
1169

    
1170
  // Redirects to confirmation form if more modules need to be enabled.
1171
  if ((!empty($more_required) || !empty($missing_modules)) && !isset($form_state['values']['confirm'])) {
1172
    $form_state['storage'] = array(
1173
      'more_required' => $more_required,
1174
      'modules' => $modules,
1175
      'missing_modules' => $missing_modules,
1176
    );
1177
    $form_state['rebuild'] = TRUE;
1178
    return;
1179
  }
1180

    
1181
  // Invokes hook_requirements('install').  If failures are detected, makes sure
1182
  // the dependent modules aren't installed either.
1183
  foreach ($modules as $name => $module) {
1184
    // Only invoke hook_requirements() on modules that are going to be installed.
1185
    if ($module['enabled'] && drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) {
1186
      if (!drupal_check_module($name)) {
1187
        $modules[$name]['enabled'] = FALSE;
1188
        foreach (array_keys($files[$name]->required_by) as $required_by) {
1189
          $modules[$required_by]['enabled'] = FALSE;
1190
        }
1191
      }
1192
    }
1193
  }
1194

    
1195
  // Initializes array of actions.
1196
  $actions = array(
1197
    'enable' => array(),
1198
    'disable' => array(),
1199
    'install' => array(),
1200
  );
1201

    
1202
  // Builds arrays of modules that need to be enabled, disabled, and installed.
1203
  foreach ($modules as $name => $module) {
1204
    if ($module['enabled']) {
1205
      if (drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) {
1206
        $actions['install'][] = $name;
1207
        $actions['enable'][] = $name;
1208
      }
1209
      elseif (!module_exists($name)) {
1210
        $actions['enable'][] = $name;
1211
      }
1212
    }
1213
    elseif (module_exists($name)) {
1214
      $actions['disable'][] = $name;
1215
    }
1216
  }
1217

    
1218
  // Gets list of modules prior to install process, unsets $form_state['storage']
1219
  // so we don't get redirected back to the confirmation form.
1220
  $pre_install_list = module_list();
1221
  unset($form_state['storage']);
1222

    
1223
  // Reverse the 'enable' list, to order dependencies before dependents.
1224
  krsort($actions['enable']);
1225

    
1226
  // Installs, enables, and disables modules.
1227
  module_enable($actions['enable'], FALSE);
1228
  module_disable($actions['disable'], FALSE);
1229

    
1230
  // Gets module list after install process, flushes caches and displays a
1231
  // message if there are changes.
1232
  $post_install_list = module_list(TRUE);
1233
  if ($pre_install_list != $post_install_list) {
1234
    drupal_flush_all_caches();
1235
    drupal_set_message(t('The configuration options have been saved.'));
1236
  }
1237

    
1238
  $form_state['redirect'] = 'admin/modules';
1239
}
1240

    
1241
/**
1242
 * Uninstall functions
1243
 */
1244

    
1245
/**
1246
 * Builds a form of currently disabled modules.
1247
 *
1248
 * @ingroup forms
1249
 * @see system_modules_uninstall_validate()
1250
 * @see system_modules_uninstall_submit()
1251
 * @param $form_state['values']
1252
 *   Submitted form values.
1253
 * @return
1254
 *   A form array representing the currently disabled modules.
1255
 */
1256
function system_modules_uninstall($form, $form_state = NULL) {
1257
  // Make sure the install API is available.
1258
  include_once DRUPAL_ROOT . '/includes/install.inc';
1259

    
1260
  // Display the confirm form if any modules have been submitted.
1261
  if (!empty($form_state['storage']) && $confirm_form = system_modules_uninstall_confirm_form($form_state['storage'])) {
1262
    return $confirm_form;
1263
  }
1264

    
1265
  // Get a list of disabled, installed modules.
1266
  $all_modules = system_rebuild_module_data();
1267
  $disabled_modules = array();
1268
  foreach ($all_modules as $name => $module) {
1269
    if (empty($module->status) && $module->schema_version > SCHEMA_UNINSTALLED) {
1270
      $disabled_modules[$name] = $module;
1271
    }
1272
  }
1273

    
1274
  // Only build the rest of the form if there are any modules available to
1275
  // uninstall.
1276
  if (!empty($disabled_modules)) {
1277
    $profile = drupal_get_profile();
1278
    uasort($disabled_modules, 'system_sort_modules_by_info_name');
1279
    $form['uninstall'] = array('#tree' => TRUE);
1280
    foreach ($disabled_modules as $module) {
1281
      $module_name = $module->info['name'] ? $module->info['name'] : $module->name;
1282
      $form['modules'][$module->name]['#module_name'] = $module_name;
1283
      $form['modules'][$module->name]['name']['#markup'] = $module_name;
1284
      $form['modules'][$module->name]['description']['#markup'] = t($module->info['description']);
1285
      $form['uninstall'][$module->name] = array(
1286
        '#type' => 'checkbox',
1287
        '#title' => t('Uninstall @module module', array('@module' => $module_name)),
1288
        '#title_display' => 'invisible',
1289
      );
1290
      // All modules which depend on this one must be uninstalled first, before
1291
      // we can allow this module to be uninstalled. (The installation profile
1292
      // is excluded from this list.)
1293
      foreach (array_keys($module->required_by) as $dependent) {
1294
        if ($dependent != $profile && drupal_get_installed_schema_version($dependent) != SCHEMA_UNINSTALLED) {
1295
          $dependent_name = isset($all_modules[$dependent]->info['name']) ? $all_modules[$dependent]->info['name'] : $dependent;
1296
          $form['modules'][$module->name]['#required_by'][] = $dependent_name;
1297
          $form['uninstall'][$module->name]['#disabled'] = TRUE;
1298
        }
1299
      }
1300
    }
1301
    $form['actions'] = array('#type' => 'actions');
1302
    $form['actions']['submit'] = array(
1303
      '#type' => 'submit',
1304
      '#value' => t('Uninstall'),
1305
    );
1306
    $form['#action'] = url('admin/modules/uninstall/confirm');
1307
  }
1308
  else {
1309
    $form['modules'] = array();
1310
  }
1311

    
1312
  return $form;
1313
}
1314

    
1315
/**
1316
 * Confirm uninstall of selected modules.
1317
 *
1318
 * @ingroup forms
1319
 * @param $storage
1320
 *   An associative array of modules selected to be uninstalled.
1321
 * @return
1322
 *   A form array representing modules to confirm.
1323
 */
1324
function system_modules_uninstall_confirm_form($storage) {
1325
  // Nothing to build.
1326
  if (empty($storage)) {
1327
    return;
1328
  }
1329

    
1330
  // Construct the hidden form elements and list items.
1331
  foreach (array_filter($storage['uninstall']) as $module => $value) {
1332
    $info = drupal_parse_info_file(drupal_get_path('module', $module) . '/' . $module . '.info');
1333
    $uninstall[] = $info['name'];
1334
    $form['uninstall'][$module] = array('#type' => 'hidden',
1335
      '#value' => 1,
1336
    );
1337
  }
1338

    
1339
  // Display a confirm form if modules have been selected.
1340
  if (isset($uninstall)) {
1341
    $form['#confirmed'] = TRUE;
1342
    $form['uninstall']['#tree'] = TRUE;
1343
    $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', array('items' => $uninstall)));
1344
    $form = confirm_form(
1345
      $form,
1346
      t('Confirm uninstall'),
1347
      'admin/modules/uninstall',
1348
      t('Would you like to continue with uninstalling the above?'),
1349
      t('Uninstall'),
1350
      t('Cancel'));
1351
    return $form;
1352
  }
1353
}
1354

    
1355
/**
1356
 * Validates the submitted uninstall form.
1357
 */
1358
function system_modules_uninstall_validate($form, &$form_state) {
1359
  // Form submitted, but no modules selected.
1360
  if (!count(array_filter($form_state['values']['uninstall']))) {
1361
    drupal_set_message(t('No modules selected.'), 'error');
1362
    drupal_goto('admin/modules/uninstall');
1363
  }
1364
}
1365

    
1366
/**
1367
 * Processes the submitted uninstall form.
1368
 */
1369
function system_modules_uninstall_submit($form, &$form_state) {
1370
  // Make sure the install API is available.
1371
  include_once DRUPAL_ROOT . '/includes/install.inc';
1372

    
1373
  if (!empty($form['#confirmed'])) {
1374
    // Call the uninstall routine for each selected module.
1375
    $modules = array_keys($form_state['values']['uninstall']);
1376
    drupal_uninstall_modules($modules);
1377
    drupal_set_message(t('The selected modules have been uninstalled.'));
1378

    
1379
    $form_state['redirect'] = 'admin/modules/uninstall';
1380
  }
1381
  else {
1382
    $form_state['storage'] = $form_state['values'];
1383
    $form_state['rebuild'] = TRUE;
1384
  }
1385
}
1386

    
1387
/**
1388
 * Menu callback. Display blocked IP addresses.
1389
 *
1390
 * @param $default_ip
1391
 *   Optional IP address to be passed on to drupal_get_form() for
1392
 *   use as the default value of the IP address form field.
1393
 */
1394
function system_ip_blocking($default_ip = '') {
1395
  $rows = array();
1396
  $header = array(t('Blocked IP addresses'), t('Operations'));
1397
  $result = db_query('SELECT * FROM {blocked_ips}');
1398
  foreach ($result as $ip) {
1399
    $rows[] = array(
1400
      $ip->ip,
1401
      l(t('delete'), "admin/config/people/ip-blocking/delete/$ip->iid"),
1402
    );
1403
  }
1404

    
1405
  $build['system_ip_blocking_form'] = drupal_get_form('system_ip_blocking_form', $default_ip);
1406

    
1407
  $build['system_ip_blocking_table'] = array(
1408
    '#theme' => 'table',
1409
    '#header' => $header,
1410
    '#rows' => $rows,
1411
    '#empty' => t('No blocked IP addresses available.'),
1412
  );
1413

    
1414
  return $build;
1415
}
1416

    
1417
/**
1418
 * Define the form for blocking IP addresses.
1419
 *
1420
 * @ingroup forms
1421
 * @see system_ip_blocking_form_validate()
1422
 * @see system_ip_blocking_form_submit()
1423
 */
1424
function system_ip_blocking_form($form, $form_state, $default_ip) {
1425
  $form['ip'] = array(
1426
    '#title' => t('IP address'),
1427
    '#type' => 'textfield',
1428
    '#size' => 48,
1429
    '#maxlength' => 40,
1430
    '#default_value' => $default_ip,
1431
    '#description' => t('Enter a valid IP address.'),
1432
  );
1433
  $form['actions'] = array('#type' => 'actions');
1434
  $form['actions']['submit'] = array(
1435
    '#type' => 'submit',
1436
    '#value' => t('Add'),
1437
  );
1438
  $form['#submit'][] = 'system_ip_blocking_form_submit';
1439
  $form['#validate'][] = 'system_ip_blocking_form_validate';
1440
  return $form;
1441
}
1442

    
1443
function system_ip_blocking_form_validate($form, &$form_state) {
1444
  $ip = trim($form_state['values']['ip']);
1445
  if (db_query("SELECT * FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField()) {
1446
    form_set_error('ip', t('This IP address is already blocked.'));
1447
  }
1448
  elseif ($ip == ip_address()) {
1449
    form_set_error('ip', t('You may not block your own IP address.'));
1450
  }
1451
  elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) {
1452
    form_set_error('ip', t('Enter a valid IP address.'));
1453
  }
1454
}
1455

    
1456
function system_ip_blocking_form_submit($form, &$form_state) {
1457
  $ip = trim($form_state['values']['ip']);
1458
  db_insert('blocked_ips')
1459
    ->fields(array('ip' => $ip))
1460
    ->execute();
1461
  drupal_set_message(t('The IP address %ip has been blocked.', array('%ip' => $ip)));
1462
  $form_state['redirect'] = 'admin/config/people/ip-blocking';
1463
  return;
1464
}
1465

    
1466
/**
1467
 * IP deletion confirm page.
1468
 *
1469
 * @see system_ip_blocking_delete_submit()
1470
 */
1471
function system_ip_blocking_delete($form, &$form_state, $iid) {
1472
  $form['blocked_ip'] = array(
1473
    '#type' => 'value',
1474
    '#value' => $iid,
1475
  );
1476
  return confirm_form($form, t('Are you sure you want to delete %ip?', array('%ip' => $iid['ip'])), 'admin/config/people/ip-blocking', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
1477
}
1478

    
1479
/**
1480
 * Process system_ip_blocking_delete form submissions.
1481
 */
1482
function system_ip_blocking_delete_submit($form, &$form_state) {
1483
  $blocked_ip = $form_state['values']['blocked_ip'];
1484
  db_delete('blocked_ips')
1485
    ->condition('iid', $blocked_ip['iid'])
1486
    ->execute();
1487
  watchdog('user', 'Deleted %ip', array('%ip' => $blocked_ip['ip']));
1488
  drupal_set_message(t('The IP address %ip was deleted.', array('%ip' => $blocked_ip['ip'])));
1489
  $form_state['redirect'] = 'admin/config/people/ip-blocking';
1490
}
1491

    
1492
/**
1493
 * Form builder; The general site information form.
1494
 *
1495
 * @ingroup forms
1496
 * @see system_settings_form()
1497
 */
1498
function system_site_information_settings() {
1499
  $form['site_information'] = array(
1500
    '#type' => 'fieldset',
1501
    '#title' => t('Site details'),
1502
  );
1503
  $form['site_information']['site_name'] = array(
1504
    '#type' => 'textfield',
1505
    '#title' => t('Site name'),
1506
    '#default_value' => variable_get('site_name', 'Drupal'),
1507
    '#required' => TRUE
1508
  );
1509
  $form['site_information']['site_slogan'] = array(
1510
    '#type' => 'textfield',
1511
    '#title' => t('Slogan'),
1512
    '#default_value' => variable_get('site_slogan', ''),
1513
    '#description' => t("How this is used depends on your site's theme."),
1514
  );
1515
  $form['site_information']['site_mail'] = array(
1516
    '#type' => 'textfield',
1517
    '#title' => t('E-mail address'),
1518
    '#default_value' => variable_get('site_mail', ini_get('sendmail_from')),
1519
    '#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.)"),
1520
    '#required' => TRUE,
1521
  );
1522
  $form['front_page'] = array(
1523
    '#type' => 'fieldset',
1524
    '#title' => t('Front page'),
1525
  );
1526
  $form['front_page']['default_nodes_main'] = array(
1527
    '#type' => 'select', '#title' => t('Number of posts on front page'),
1528
    '#default_value' => variable_get('default_nodes_main', 10),
1529
    '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)),
1530
    '#description' => t('The maximum number of posts displayed on overview pages such as the front page.')
1531
  );
1532
  $form['front_page']['site_frontpage'] = array(
1533
    '#type' => 'textfield',
1534
    '#title' => t('Default front page'),
1535
    '#default_value' => (variable_get('site_frontpage')!='node'?drupal_get_path_alias(variable_get('site_frontpage', 'node')):''),
1536
    '#size' => 40,
1537
    '#description' => t('Optionally, specify a relative URL to display as the front page.  Leave blank to display the default content feed.'),
1538
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
1539
  );
1540
  $form['error_page'] = array(
1541
    '#type' => 'fieldset',
1542
    '#title' => t('Error pages'),
1543
  );
1544
  $form['error_page']['site_403'] = array(
1545
    '#type' => 'textfield',
1546
    '#title' => t('Default 403 (access denied) page'),
1547
    '#default_value' => variable_get('site_403', ''),
1548
    '#size' => 40,
1549
    '#description' => t('This page is displayed when the requested document is denied to the current user. Leave blank to display a generic "access denied" page.'),
1550
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=')
1551
  );
1552
  $form['error_page']['site_404'] = array(
1553
    '#type' => 'textfield',
1554
    '#title' => t('Default 404 (not found) page'),
1555
    '#default_value' => variable_get('site_404', ''),
1556
    '#size' => 40,
1557
    '#description' => t('This page is displayed when no other content matches the requested document. Leave blank to display a generic "page not found" page.'),
1558
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=')
1559
  );
1560

    
1561
  $form['#validate'][] = 'system_site_information_settings_validate';
1562

    
1563
  return system_settings_form($form);
1564
}
1565

    
1566
/**
1567
 * Validates the submitted site-information form.
1568
 */
1569
function system_site_information_settings_validate($form, &$form_state) {
1570
  // Validate the e-mail address.
1571
  if ($error = user_validate_mail($form_state['values']['site_mail'])) {
1572
    form_set_error('site_mail', $error);
1573
  }
1574
  // Check for empty front page path.
1575
  if (empty($form_state['values']['site_frontpage'])) {
1576
    // Set to default "node".
1577
    form_set_value($form['front_page']['site_frontpage'], 'node', $form_state);
1578
  }
1579
  else {
1580
    // Get the normal path of the front page.
1581
    form_set_value($form['front_page']['site_frontpage'], drupal_get_normal_path($form_state['values']['site_frontpage']), $form_state);
1582
  }
1583
  // Validate front page path.
1584
  if (!drupal_valid_path($form_state['values']['site_frontpage'])) {
1585
    form_set_error('site_frontpage', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_frontpage'])));
1586
  }
1587
  // Get the normal paths of both error pages.
1588
  if (!empty($form_state['values']['site_403'])) {
1589
    form_set_value($form['error_page']['site_403'], drupal_get_normal_path($form_state['values']['site_403']), $form_state);
1590
  }
1591
  if (!empty($form_state['values']['site_404'])) {
1592
    form_set_value($form['error_page']['site_404'], drupal_get_normal_path($form_state['values']['site_404']), $form_state);
1593
  }
1594
  // Validate 403 error path.
1595
  if (!empty($form_state['values']['site_403']) && !drupal_valid_path($form_state['values']['site_403'])) {
1596
    form_set_error('site_403', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_403'])));
1597
  }
1598
  // Validate 404 error path.
1599
  if (!empty($form_state['values']['site_404']) && !drupal_valid_path($form_state['values']['site_404'])) {
1600
    form_set_error('site_404', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_404'])));
1601
  }
1602
}
1603

    
1604
/**
1605
 * Form builder; Cron form.
1606
 *
1607
 * @see system_settings_form()
1608
 * @ingroup forms
1609
 */
1610
function system_cron_settings() {
1611
  global $base_url;
1612
  $form['description'] = array(
1613
    '#markup' => '<p>' . t('Cron takes care of running periodic tasks like checking for updates and indexing content for search.') . '</p>',
1614
  );
1615
  $form['run'] = array(
1616
    '#type' => 'submit',
1617
    '#value' => t('Run cron'),
1618
    '#submit' => array('system_run_cron_submit'),
1619
  );
1620
  $status = '<p>' . t('Last run: %cron-last ago.', array('%cron-last' => format_interval(REQUEST_TIME - variable_get('cron_last')),)) . '</p>';
1621
  $form['status'] = array(
1622
    '#markup' => $status,
1623
  );
1624

    
1625
  $form['cron_url'] = array(
1626
    '#markup' => '<p>' . t('To run cron from outside the site, go to <a href="!cron">!cron</a>', array('!cron' => url($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => variable_get('cron_key', 'drupal')))))) . '</p>',
1627
  );
1628

    
1629
  $form['cron'] = array(
1630
    '#type' => 'fieldset',
1631
  );
1632
  $form['cron']['cron_safe_threshold'] = array(
1633
    '#type' => 'select',
1634
    '#title' => t('Run cron every'),
1635
    '#description' => t('More information about setting up scheduled tasks can be found by <a href="@url">reading the cron tutorial on drupal.org</a>.', array('@url' => url('http://drupal.org/cron'))),
1636
    '#default_value' => variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD),
1637
    '#options' => array(0 => t('Never')) + drupal_map_assoc(array(3600, 10800, 21600, 43200, 86400, 604800), 'format_interval'),
1638
  );
1639

    
1640
  return system_settings_form($form);
1641
}
1642

    
1643
/**
1644
 * Submit callback; run cron.
1645
 *
1646
 * @ingroup forms
1647
 */
1648
function system_run_cron_submit($form, &$form_state) {
1649
  // Run cron manually from Cron form.
1650
  if (drupal_cron_run()) {
1651
    drupal_set_message(t('Cron run successfully.'));
1652
  }
1653
  else {
1654
    drupal_set_message(t('Cron run failed.'), 'error');
1655
  }
1656

    
1657
  drupal_goto('admin/config/system/cron');
1658
}
1659

    
1660
/**
1661
 * Form builder; Configure error reporting settings.
1662
 *
1663
 * @ingroup forms
1664
 * @see system_settings_form()
1665
 */
1666
function system_logging_settings() {
1667
  $form['error_level'] = array(
1668
    '#type' => 'radios',
1669
    '#title' => t('Error messages to display'),
1670
    '#default_value' => variable_get('error_level', ERROR_REPORTING_DISPLAY_ALL),
1671
    '#options' => array(
1672
      ERROR_REPORTING_HIDE => t('None'),
1673
      ERROR_REPORTING_DISPLAY_SOME => t('Errors and warnings'),
1674
      ERROR_REPORTING_DISPLAY_ALL => t('All messages'),
1675
    ),
1676
    '#description' => t('It is recommended that sites running on production environments do not display any errors.'),
1677
  );
1678

    
1679
  return system_settings_form($form);
1680
}
1681

    
1682
/**
1683
 * Form builder; Configure site performance settings.
1684
 *
1685
 * @ingroup forms
1686
 * @see system_settings_form()
1687
 */
1688
function system_performance_settings() {
1689
  drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
1690

    
1691
  $form['clear_cache'] = array(
1692
    '#type' => 'fieldset',
1693
    '#title' => t('Clear cache'),
1694
  );
1695

    
1696
  $form['clear_cache']['clear'] = array(
1697
    '#type' => 'submit',
1698
    '#value' => t('Clear all caches'),
1699
    '#submit' => array('system_clear_cache_submit'),
1700
  );
1701

    
1702
  $form['caching'] = array(
1703
    '#type' => 'fieldset',
1704
    '#title' => t('Caching'),
1705
  );
1706

    
1707
  $cache = variable_get('cache', 0);
1708
  $form['caching']['cache'] = array(
1709
    '#type' => 'checkbox',
1710
    '#title' => t('Cache pages for anonymous users'),
1711
    '#default_value' => $cache,
1712
    '#weight' => -2,
1713
  );
1714
  $period = drupal_map_assoc(array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400), 'format_interval');
1715
  $period[0] = '<' . t('none') . '>';
1716
  $form['caching']['cache_lifetime'] = array(
1717
    '#type' => 'select',
1718
    '#title' => t('Minimum cache lifetime'),
1719
    '#default_value' => variable_get('cache_lifetime', 0),
1720
    '#options' => $period,
1721
    '#description' => t('Cached pages will not be re-created until at least this much time has elapsed.')
1722
  );
1723
  $form['caching']['page_cache_maximum_age'] = array(
1724
    '#type' => 'select',
1725
    '#title' => t('Expiration of cached pages'),
1726
    '#default_value' => variable_get('page_cache_maximum_age', 0),
1727
    '#options' => $period,
1728
    '#description' => t('The maximum time an external cache can use an old version of a page.')
1729
  );
1730

    
1731
  $directory = 'public://';
1732
  $is_writable = is_dir($directory) && is_writable($directory);
1733
  $disabled = !$is_writable;
1734
  $disabled_message = '';
1735
  if (!$is_writable) {
1736
    $disabled_message = ' ' . t('<strong class="error">Set up the <a href="!file-system">public files directory</a> to make these optimizations available.</strong>', array('!file-system' => url('admin/config/media/file-system')));
1737
  }
1738

    
1739
  $form['bandwidth_optimization'] = array(
1740
    '#type' => 'fieldset',
1741
    '#title' => t('Bandwidth optimization'),
1742
    '#description' => t('External resources can be optimized automatically, which can reduce both the size and number of requests made to your website.') . $disabled_message,
1743
  );
1744

    
1745
  $js_hide = $cache ? '' : ' class="js-hide"';
1746
  $form['bandwidth_optimization']['page_compression'] = array(
1747
    '#type' => 'checkbox',
1748
    '#title' => t('Compress cached pages.'),
1749
    '#default_value' => variable_get('page_compression', TRUE),
1750
    '#prefix' => '<div id="page-compression-wrapper"' . $js_hide . '>',
1751
    '#suffix' => '</div>',
1752
  );
1753
  $form['bandwidth_optimization']['preprocess_css'] = array(
1754
    '#type' => 'checkbox',
1755
    '#title' => t('Aggregate and compress CSS files.'),
1756
    '#default_value' => intval(variable_get('preprocess_css', 0) && $is_writable),
1757
    '#disabled' => $disabled,
1758
  );
1759
  $form['bandwidth_optimization']['preprocess_js'] = array(
1760
    '#type' => 'checkbox',
1761
    '#title' => t('Aggregate JavaScript files.'),
1762
    '#default_value' => intval(variable_get('preprocess_js', 0) && $is_writable),
1763
    '#disabled' => $disabled,
1764
  );
1765

    
1766
  $form['#submit'][] = 'drupal_clear_css_cache';
1767
  $form['#submit'][] = 'drupal_clear_js_cache';
1768
  // This form allows page compression settings to be changed, which can
1769
  // invalidate the page cache, so it needs to be cleared on form submit.
1770
  $form['#submit'][] = 'system_clear_page_cache_submit';
1771

    
1772
  return system_settings_form($form);
1773
}
1774

    
1775
/**
1776
 * Submit callback; clear system caches.
1777
 *
1778
 * @ingroup forms
1779
 */
1780
function system_clear_cache_submit($form, &$form_state) {
1781
  drupal_flush_all_caches();
1782
  drupal_set_message(t('Caches cleared.'));
1783
}
1784

    
1785
/**
1786
 * Submit callback; clear the page cache.
1787
 *
1788
 * @ingroup forms
1789
 */
1790
function system_clear_page_cache_submit($form, &$form_state) {
1791
  cache_clear_all('*', 'cache_page', TRUE);
1792
}
1793

    
1794
/**
1795
 * Form builder; Configure the site file handling.
1796
 *
1797
 * @ingroup forms
1798
 * @see system_settings_form()
1799
 */
1800
function system_file_system_settings() {
1801
  $form['file_public_path'] = array(
1802
    '#type' => 'textfield',
1803
    '#title' => t('Public file system path'),
1804
    '#default_value' => variable_get('file_public_path', conf_path() . '/files'),
1805
    '#maxlength' => 255,
1806
    '#description' => t('A local file system path where public files will be stored. This directory must exist and be writable by Drupal. This directory must be relative to the Drupal installation directory and be accessible over the web.'),
1807
    '#after_build' => array('system_check_directory'),
1808
  );
1809

    
1810
  $form['file_private_path'] = array(
1811
    '#type' => 'textfield',
1812
    '#title' => t('Private file system path'),
1813
    '#default_value' => variable_get('file_private_path', ''),
1814
    '#maxlength' => 255,
1815
    '#description' => t('An existing local file system path for storing private files. It should be writable by Drupal and not accessible over the web. See the online handbook for <a href="@handbook">more information about securing private files</a>.', array('@handbook' => 'http://drupal.org/documentation/modules/file')),
1816
    '#after_build' => array('system_check_directory'),
1817
  );
1818

    
1819
  $form['file_temporary_path'] = array(
1820
    '#type' => 'textfield',
1821
    '#title' => t('Temporary directory'),
1822
    '#default_value' => variable_get('file_temporary_path', file_directory_temp()),
1823
    '#maxlength' => 255,
1824
    '#description' => t('A local file system path where temporary files will be stored. This directory should not be accessible over the web.'),
1825
    '#after_build' => array('system_check_directory'),
1826
  );
1827
  // Any visible, writeable wrapper can potentially be used for the files
1828
  // directory, including a remote file system that integrates with a CDN.
1829
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
1830
    $options[$scheme] = check_plain($info['description']);
1831
  }
1832

    
1833
  if (!empty($options)) {
1834
    $form['file_default_scheme'] = array(
1835
      '#type' => 'radios',
1836
      '#title' => t('Default download method'),
1837
      '#default_value' => variable_get('file_default_scheme', isset($options['public']) ? 'public' : key($options)),
1838
      '#options' => $options,
1839
      '#description' => t('This setting is used as the preferred download method. The use of public files is more efficient, but does not provide any access control.'),
1840
    );
1841
  }
1842

    
1843
  return system_settings_form($form);
1844
}
1845

    
1846
/**
1847
 * Form builder; Configure site image toolkit usage.
1848
 *
1849
 * @ingroup forms
1850
 * @see system_settings_form()
1851
 */
1852
function system_image_toolkit_settings() {
1853
  $toolkits_available = image_get_available_toolkits();
1854
  $current_toolkit = image_get_toolkit();
1855

    
1856
  if (count($toolkits_available) == 0) {
1857
    variable_del('image_toolkit');
1858
    $form['image_toolkit_help'] = array(
1859
      '#markup' => t("No image toolkits were detected. Drupal includes support for <a href='!gd-link'>PHP's built-in image processing functions</a> but they were not detected on this system. You should consult your system administrator to have them enabled, or try using a third party toolkit.", array('!gd-link' => url('http://php.net/gd'))),
1860
    );
1861
    return $form;
1862
  }
1863

    
1864
  if (count($toolkits_available) > 1) {
1865
    $form['image_toolkit'] = array(
1866
      '#type' => 'radios',
1867
      '#title' => t('Select an image processing toolkit'),
1868
      '#default_value' => variable_get('image_toolkit', $current_toolkit),
1869
      '#options' => $toolkits_available
1870
    );
1871
  }
1872
  else {
1873
    variable_set('image_toolkit', key($toolkits_available));
1874
  }
1875

    
1876
  // Get the toolkit's settings form.
1877
  $function = 'image_' . $current_toolkit . '_settings';
1878
  if (function_exists($function)) {
1879
    $form['image_toolkit_settings'] = $function();
1880
  }
1881

    
1882
  return system_settings_form($form);
1883
}
1884

    
1885
/**
1886
 * Form builder; Configure how the site handles RSS feeds.
1887
 *
1888
 * @ingroup forms
1889
 * @see system_settings_form()
1890
 */
1891
function system_rss_feeds_settings() {
1892
  $form['feed_description'] = array(
1893
    '#type' => 'textarea',
1894
    '#title' => t('Feed description'),
1895
    '#default_value' => variable_get('feed_description', ''),
1896
    '#description' => t('Description of your site, included in each feed.')
1897
  );
1898
  $form['feed_default_items'] = array(
1899
    '#type' => 'select',
1900
    '#title' => t('Number of items in each feed'),
1901
    '#default_value' => variable_get('feed_default_items', 10),
1902
    '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)),
1903
    '#description' => t('Default number of items to include in each feed.')
1904
  );
1905
  $form['feed_item_length'] = array(
1906
    '#type' => 'select',
1907
    '#title' => t('Feed content'),
1908
    '#default_value' => variable_get('feed_item_length', 'fulltext'),
1909
    '#options' => array('title' => t('Titles only'), 'teaser' => t('Titles plus teaser'), 'fulltext' => t('Full text')),
1910
    '#description' => t('Global setting for the default display of content items in each feed.')
1911
  );
1912

    
1913
  return system_settings_form($form);
1914
}
1915

    
1916
/**
1917
 * Form builder; Configure the site regional settings.
1918
 *
1919
 * @ingroup forms
1920
 * @see system_settings_form()
1921
 * @see system_regional_settings_submit()
1922
 */
1923
function system_regional_settings() {
1924
  include_once DRUPAL_ROOT . '/includes/locale.inc';
1925
  $countries = country_get_list();
1926

    
1927
  // Date settings:
1928
  $zones = system_time_zones();
1929

    
1930
  $form['locale'] = array(
1931
    '#type' => 'fieldset',
1932
    '#title' => t('Locale'),
1933
  );
1934

    
1935
  $form['locale']['site_default_country'] = array(
1936
    '#type' => 'select',
1937
    '#title' => t('Default country'),
1938
    '#empty_value' => '',
1939
    '#default_value' => variable_get('site_default_country', ''),
1940
    '#options' => $countries,
1941
    '#attributes' => array('class' => array('country-detect')),
1942
  );
1943

    
1944
  $form['locale']['date_first_day'] = array(
1945
    '#type' => 'select',
1946
    '#title' => t('First day of week'),
1947
    '#default_value' => variable_get('date_first_day', 0),
1948
    '#options' => array(0 => t('Sunday'), 1 => t('Monday'), 2 => t('Tuesday'), 3 => t('Wednesday'), 4 => t('Thursday'), 5 => t('Friday'), 6 => t('Saturday')),
1949
  );
1950

    
1951
  $form['timezone'] = array(
1952
    '#type' => 'fieldset',
1953
    '#title' => t('Time zones'),
1954
  );
1955

    
1956
  $form['timezone']['date_default_timezone'] = array(
1957
    '#type' => 'select',
1958
    '#title' => t('Default time zone'),
1959
    '#default_value' => variable_get('date_default_timezone', date_default_timezone_get()),
1960
    '#options' => $zones,
1961
  );
1962

    
1963
  $configurable_timezones = variable_get('configurable_timezones', 1);
1964
  $form['timezone']['configurable_timezones'] = array(
1965
    '#type' => 'checkbox',
1966
    '#title' => t('Users may set their own time zone.'),
1967
    '#default_value' => $configurable_timezones,
1968
  );
1969

    
1970
  $form['timezone']['configurable_timezones_wrapper'] =  array(
1971
    '#type' => 'container',
1972
    '#states' => array(
1973
      // Hide the user configured timezone settings when users are forced to use
1974
      // the default setting.
1975
      'invisible' => array(
1976
        'input[name="configurable_timezones"]' => array('checked' => FALSE),
1977
      ),
1978
    ),
1979
  );
1980
  $form['timezone']['configurable_timezones_wrapper']['empty_timezone_message'] = array(
1981
    '#type' => 'checkbox',
1982
    '#title' => t('Remind users at login if their time zone is not set.'),
1983
    '#default_value' => variable_get('empty_timezone_message', 0),
1984
    '#description' => t('Only applied if users may set their own time zone.')
1985
  );
1986

    
1987
  $form['timezone']['configurable_timezones_wrapper']['user_default_timezone'] = array(
1988
    '#type' => 'radios',
1989
    '#title' => t('Time zone for new users'),
1990
    '#default_value' => variable_get('user_default_timezone', DRUPAL_USER_TIMEZONE_DEFAULT),
1991
    '#options' => array(
1992
      DRUPAL_USER_TIMEZONE_DEFAULT => t('Default time zone.'),
1993
      DRUPAL_USER_TIMEZONE_EMPTY   => t('Empty time zone.'),
1994
      DRUPAL_USER_TIMEZONE_SELECT  => t('Users may set their own time zone at registration.'),
1995
    ),
1996
    '#description' => t('Only applied if users may set their own time zone.')
1997
  );
1998

    
1999
  return system_settings_form($form);
2000
}
2001

    
2002
/**
2003
 * Form builder; Configure the site date and time settings.
2004
 *
2005
 * @ingroup forms
2006
 * @see system_settings_form()
2007
 */
2008
function system_date_time_settings() {
2009
  // Get list of all available date types.
2010
  drupal_static_reset('system_get_date_types');
2011
  $format_types = system_get_date_types();
2012

    
2013
  // Get list of all available date formats.
2014
  $all_formats = array();
2015
  drupal_static_reset('system_get_date_formats');
2016
  $date_formats = system_get_date_formats(); // Call this to rebuild the list, and to have default list.
2017
  foreach ($date_formats as $type => $format_info) {
2018
    $all_formats = array_merge($all_formats, $format_info);
2019
  }
2020
  $custom_formats = system_get_date_formats('custom');
2021
  if (!empty($format_types)) {
2022
    foreach ($format_types as $type => $type_info) {
2023
      // If a system type, only show the available formats for that type and
2024
      // custom ones.
2025
      if ($type_info['locked'] == 1) {
2026
        $formats = system_get_date_formats($type);
2027
        if (empty($formats)) {
2028
          $formats = $all_formats;
2029
        }
2030
        elseif (!empty($custom_formats)) {
2031
          $formats = array_merge($formats, $custom_formats);
2032
        }
2033
      }
2034
      // If a user configured type, show all available date formats.
2035
      else {
2036
        $formats = $all_formats;
2037
      }
2038

    
2039
      $choices = array();
2040
      foreach ($formats as $f => $format) {
2041
        $choices[$f] = format_date(REQUEST_TIME, 'custom', $f);
2042
      }
2043
      reset($formats);
2044
      $default = variable_get('date_format_' . $type, key($formats));
2045

    
2046
      // Get date type info for this date type.
2047
      $type_info = system_get_date_types($type);
2048
      $form['formats']['#theme'] = 'system_date_time_settings';
2049

    
2050
      // Show date format select list.
2051
      $form['formats']['format']['date_format_' . $type] = array(
2052
        '#type' => 'select',
2053
        '#title' => check_plain($type_info['title']),
2054
        '#attributes' => array('class' => array('date-format')),
2055
        '#default_value' => (isset($choices[$default]) ? $default : 'custom'),
2056
        '#options' => $choices,
2057
      );
2058

    
2059
      // If this isn't a system provided type, allow the user to remove it from
2060
      // the system.
2061
      if ($type_info['locked'] == 0) {
2062
        $form['formats']['delete']['date_format_' . $type . '_delete'] = array(
2063
          '#type' => 'link',
2064
          '#title' => t('delete'),
2065
          '#href' => 'admin/config/regional/date-time/types/' . $type . '/delete',
2066
        );
2067
      }
2068
    }
2069
  }
2070

    
2071
  // Display a message if no date types configured.
2072
  $form['#empty_text'] = t('No date types available. <a href="@link">Add date type</a>.', array('@link' => url('admin/config/regional/date-time/types/add')));
2073

    
2074
  return system_settings_form($form);
2075
}
2076

    
2077
/**
2078
 * Returns HTML for the date settings form.
2079
 *
2080
 * @param $variables
2081
 *   An associative array containing:
2082
 *   - form: A render element representing the form.
2083
 *
2084
 * @ingroup themeable
2085
 */
2086
function theme_system_date_time_settings($variables) {
2087
  $form = $variables['form'];
2088
  $header = array(
2089
    t('Date type'),
2090
    t('Format'),
2091
    t('Operations'),
2092
  );
2093

    
2094
  foreach (element_children($form['format']) as $key) {
2095
    $delete_key = $key . '_delete';
2096
    $row = array();
2097
    $row[] = $form['format'][$key]['#title'];
2098
    $form['format'][$key]['#title_display'] = 'invisible';
2099
    $row[] = array('data' => drupal_render($form['format'][$key]));
2100
    $row[] = array('data' => drupal_render($form['delete'][$delete_key]));
2101
    $rows[] = $row;
2102
  }
2103

    
2104
  $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'system-date-types')));
2105
  $output .= drupal_render_children($form);
2106

    
2107
  return $output;
2108
}
2109

    
2110

    
2111
/**
2112
 * Add new date type.
2113
 *
2114
 * @ingroup forms
2115
 * @ingroup system_add_date_format_type_form_validate()
2116
 * @ingroup system_add_date_format_type_form_submit()
2117
 */
2118
function system_add_date_format_type_form($form, &$form_state) {
2119
  $form['date_type'] = array(
2120
    '#title' => t('Date type'),
2121
    '#type' => 'textfield',
2122
    '#required' => TRUE,
2123
  );
2124
  $form['machine_name'] = array(
2125
    '#type' => 'machine_name',
2126
    '#machine_name' => array(
2127
      'exists' => 'system_get_date_types',
2128
      'source' => array('date_type'),
2129
    ),
2130
  );
2131

    
2132
  // Get list of all available date formats.
2133
  $formats = array();
2134
  drupal_static_reset('system_get_date_formats');
2135
  $date_formats = system_get_date_formats(); // Call this to rebuild the list, and to have default list.
2136
  foreach ($date_formats as $type => $format_info) {
2137
    $formats = array_merge($formats, $format_info);
2138
  }
2139
  $custom_formats = system_get_date_formats('custom');
2140
  if (!empty($custom_formats)) {
2141
    $formats = array_merge($formats, $custom_formats);
2142
  }
2143
  $choices = array();
2144
  foreach ($formats as $f => $format) {
2145
    $choices[$f] = format_date(REQUEST_TIME, 'custom', $f);
2146
  }
2147
  // Show date format select list.
2148
  $form['date_format'] = array(
2149
    '#type' => 'select',
2150
    '#title' => t('Date format'),
2151
    '#attributes' => array('class' => array('date-format')),
2152
    '#options' => $choices,
2153
    '#required' => TRUE,
2154
  );
2155

    
2156
  $form['actions'] = array('#type' => 'actions');
2157
  $form['actions']['submit'] = array(
2158
    '#type' => 'submit',
2159
    '#value' => t('Add date type'),
2160
  );
2161

    
2162
  $form['#validate'][] = 'system_add_date_format_type_form_validate';
2163
  $form['#submit'][] = 'system_add_date_format_type_form_submit';
2164

    
2165
  return $form;
2166
}
2167

    
2168
/**
2169
 * Validate system_add_date_format_type form submissions.
2170
 */
2171
function system_add_date_format_type_form_validate($form, &$form_state) {
2172
  if (!empty($form_state['values']['machine_name']) && !empty($form_state['values']['date_type'])) {
2173
    if (!preg_match("/^[a-zA-Z0-9_]+$/", trim($form_state['values']['machine_name']))) {
2174
      form_set_error('machine_name', t('The date type must contain only alphanumeric characters and underscores.'));
2175
    }
2176
    $types = system_get_date_types();
2177
    if (in_array(trim($form_state['values']['machine_name']), array_keys($types))) {
2178
      form_set_error('machine_name', t('This date type already exists. Enter a unique type.'));
2179
    }
2180
  }
2181
}
2182

    
2183
/**
2184
 * Process system_add_date_format_type form submissions.
2185
 */
2186
function system_add_date_format_type_form_submit($form, &$form_state) {
2187
  $machine_name = trim($form_state['values']['machine_name']);
2188

    
2189
  $format_type = array();
2190
  $format_type['title'] = trim($form_state['values']['date_type']);
2191
  $format_type['type'] = $machine_name;
2192
  $format_type['locked'] = 0;
2193
  $format_type['is_new'] = 1;
2194
  system_date_format_type_save($format_type);
2195
  variable_set('date_format_' . $machine_name, $form_state['values']['date_format']);
2196

    
2197
  drupal_set_message(t('New date type added successfully.'));
2198
  $form_state['redirect'] = 'admin/config/regional/date-time';
2199
}
2200

    
2201
/**
2202
 * Return the date for a given format string via Ajax.
2203
 */
2204
function system_date_time_lookup() {
2205
  // This callback is protected with a CSRF token because user input from the
2206
  // query string is reflected in the output.
2207
  if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], 'admin/config/regional/date-time/formats/lookup')) {
2208
    return MENU_ACCESS_DENIED;
2209
  }
2210
  $result = format_date(REQUEST_TIME, 'custom', $_GET['format']);
2211
  drupal_json_output($result);
2212
}
2213

    
2214
/**
2215
 * Form builder; Configure the site's maintenance status.
2216
 *
2217
 * @ingroup forms
2218
 * @see system_settings_form()
2219
 */
2220
function system_site_maintenance_mode() {
2221
  $form['maintenance_mode'] = array(
2222
    '#type' => 'checkbox',
2223
    '#title' => t('Put site into maintenance mode'),
2224
    '#default_value' => variable_get('maintenance_mode', 0),
2225
    '#description' => t('When enabled, only users with the "Use the site in maintenance mode" <a href="@permissions-url">permission</a> are able to access your site to perform maintenance; all other visitors see the maintenance mode message configured below. Authorized users can log in directly via the <a href="@user-login">user login</a> page.', array('@permissions-url' => url('admin/people/permissions'), '@user-login' => url('user'))),
2226
  );
2227
  $form['maintenance_mode_message'] = array(
2228
    '#type' => 'textarea',
2229
    '#title' => t('Maintenance mode message'),
2230
    '#default_value' => variable_get('maintenance_mode_message', t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')))),
2231
    '#description' => t('Message to show visitors when the site is in maintenance mode.')
2232
  );
2233

    
2234
  return system_settings_form($form);
2235
}
2236

    
2237
/**
2238
 * Form builder; Configure clean URL settings.
2239
 *
2240
 * @ingroup forms
2241
 * @see system_settings_form()
2242
 */
2243
function system_clean_url_settings($form, &$form_state) {
2244
  $available = FALSE;
2245
  $conflict = FALSE;
2246

    
2247
  // If the request URI is a clean URL, clean URLs must be available.
2248
  // Otherwise, run a test.
2249
  if (strpos(request_uri(), '?q=') === FALSE && strpos(request_uri(), '&q=') === FALSE) {
2250
    $available = TRUE;
2251
  }
2252
  else {
2253
    $request = drupal_http_request($GLOBALS['base_url'] . '/admin/config/search/clean-urls/check');
2254
    // If the request returns HTTP 200, clean URLs are available.
2255
    if (isset($request->code) && $request->code == 200) {
2256
      $available = TRUE;
2257
      // If the user started the clean URL test, provide explicit feedback.
2258
      if (isset($form_state['input']['clean_url_test_execute'])) {
2259
        drupal_set_message(t('The clean URL test passed.'));
2260
      }
2261
    }
2262
    else {
2263
      // If the test failed while clean URLs are enabled, make sure clean URLs
2264
      // can be disabled.
2265
      if (variable_get('clean_url', 0)) {
2266
        $conflict = TRUE;
2267
        // Warn the user of a conflicting situation, unless after processing
2268
        // a submitted form.
2269
        if (!isset($form_state['input']['op'])) {
2270
          drupal_set_message(t('Clean URLs are enabled, but the clean URL test failed. Uncheck the box below to disable clean URLs.'), 'warning');
2271
        }
2272
      }
2273
      // If the user started the clean URL test, provide explicit feedback.
2274
      elseif (isset($form_state['input']['clean_url_test_execute'])) {
2275
        drupal_set_message(t('The clean URL test failed.'), 'warning');
2276
      }
2277
    }
2278
  }
2279

    
2280
  // Show the enable/disable form if clean URLs are available or if the user
2281
  // must be able to resolve a conflicting setting.
2282
  if ($available || $conflict) {
2283
    $form['clean_url'] = array(
2284
      '#type' => 'checkbox',
2285
      '#title' => t('Enable clean URLs'),
2286
      '#default_value' => variable_get('clean_url', 0),
2287
      '#description' => t('Use URLs like <code>example.com/user</code> instead of <code>example.com/?q=user</code>.'),
2288
    );
2289
    $form = system_settings_form($form);
2290
    if ($conflict) {
2291
      // $form_state['redirect'] needs to be set to the non-clean URL,
2292
      // otherwise the setting is not saved.
2293
      $form_state['redirect'] = url('', array('query' => array('q' => '/admin/config/search/clean-urls')));
2294
    }
2295
  }
2296
  // Show the clean URLs test form.
2297
  else {
2298
    drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
2299

    
2300
    $form_state['redirect'] = url('admin/config/search/clean-urls');
2301
    $form['clean_url_description'] = array(
2302
      '#type' => 'markup',
2303
      '#markup' => '<p>' . t('Use URLs like <code>example.com/user</code> instead of <code>example.com/?q=user</code>.'),
2304
    );
2305
    // Explain why the user is seeing this page and what to expect after
2306
    // clicking the 'Run the clean URL test' button.
2307
    $form['clean_url_test_result'] = array(
2308
      '#type' => 'markup',
2309
      '#markup' => '<p>' . t('Clean URLs cannot be enabled. If you are directed to this page or to a <em>Page not found (404)</em> error after testing for clean URLs, see the <a href="@handbook">online handbook</a>.', array('@handbook' => 'http://drupal.org/node/15365')) . '</p>',
2310
    );
2311
    $form['actions'] = array(
2312
      '#type' => 'actions',
2313
      'clean_url_test' => array(
2314
        '#type' => 'submit',
2315
        '#value' => t('Run the clean URL test'),
2316
      ),
2317
    );
2318
    $form['clean_url_test_execute'] = array(
2319
      '#type' => 'hidden',
2320
      '#value' => 1,
2321
    );
2322
  }
2323

    
2324
  return $form;
2325
}
2326

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

    
2338
  // Check run-time requirements and status information.
2339
  $requirements = module_invoke_all('requirements', 'runtime');
2340
  usort($requirements, '_system_sort_requirements');
2341

    
2342
  if ($check) {
2343
    return drupal_requirements_severity($requirements) == REQUIREMENT_ERROR;
2344
  }
2345
  // MySQL import might have set the uid of the anonymous user to autoincrement
2346
  // value. Let's try fixing it. See http://drupal.org/node/204411
2347
  db_update('users')
2348
    ->expression('uid', 'uid - uid')
2349
    ->condition('name', '')
2350
    ->condition('pass', '')
2351
    ->condition('status', 0)
2352
    ->execute();
2353
  return theme('status_report', array('requirements' => $requirements));
2354
}
2355

    
2356
/**
2357
 * Menu callback: run cron manually.
2358
 */
2359
function system_run_cron() {
2360
  // Run cron manually
2361
  if (drupal_cron_run()) {
2362
    drupal_set_message(t('Cron ran successfully.'));
2363
  }
2364
  else {
2365
    drupal_set_message(t('Cron run failed.'), 'error');
2366
  }
2367

    
2368
  drupal_goto('admin/reports/status');
2369
}
2370

    
2371
/**
2372
 * Menu callback: return information about PHP.
2373
 */
2374
function system_php() {
2375
  phpinfo();
2376
  drupal_exit();
2377
}
2378

    
2379
/**
2380
 * Default page callback for batches.
2381
 */
2382
function system_batch_page() {
2383
  require_once DRUPAL_ROOT . '/includes/batch.inc';
2384
  $output = _batch_page();
2385

    
2386
  if ($output === FALSE) {
2387
    drupal_access_denied();
2388
  }
2389
  elseif (isset($output)) {
2390
    // Force a page without blocks or messages to
2391
    // display a list of collected messages later.
2392
    drupal_set_page_content($output);
2393
    $page = element_info('page');
2394
    $page['#show_messages'] = FALSE;
2395
    return $page;
2396
  }
2397
}
2398

    
2399
/**
2400
 * Returns HTML for an administrative block for display.
2401
 *
2402
 * @param $variables
2403
 *   An associative array containing:
2404
 *   - block: An array containing information about the block:
2405
 *     - show: A Boolean whether to output the block. Defaults to FALSE.
2406
 *     - title: The block's title.
2407
 *     - content: (optional) Formatted content for the block.
2408
 *     - description: (optional) Description of the block. Only output if
2409
 *       'content' is not set.
2410
 *
2411
 * @ingroup themeable
2412
 */
2413
function theme_admin_block($variables) {
2414
  $block = $variables['block'];
2415
  $output = '';
2416

    
2417
  // Don't display the block if it has no content to display.
2418
  if (empty($block['show'])) {
2419
    return $output;
2420
  }
2421

    
2422
  $output .= '<div class="admin-panel">';
2423
  if (!empty($block['title'])) {
2424
    $output .= '<h3>' . $block['title'] . '</h3>';
2425
  }
2426
  if (!empty($block['content'])) {
2427
    $output .= '<div class="body">' . $block['content'] . '</div>';
2428
  }
2429
  else {
2430
    $output .= '<div class="description">' . $block['description'] . '</div>';
2431
  }
2432
  $output .= '</div>';
2433

    
2434
  return $output;
2435
}
2436

    
2437
/**
2438
 * Returns HTML for the content of an administrative block.
2439
 *
2440
 * @param $variables
2441
 *   An associative array containing:
2442
 *   - content: An array containing information about the block. Each element
2443
 *     of the array represents an administrative menu item, and must at least
2444
 *     contain the keys 'title', 'href', and 'localized_options', which are
2445
 *     passed to l(). A 'description' key may also be provided.
2446
 *
2447
 * @ingroup themeable
2448
 */
2449
function theme_admin_block_content($variables) {
2450
  $content = $variables['content'];
2451
  $output = '';
2452

    
2453
  if (!empty($content)) {
2454
    $class = 'admin-list';
2455
    if ($compact = system_admin_compact_mode()) {
2456
      $class .= ' compact';
2457
    }
2458
    $output .= '<dl class="' . $class . '">';
2459
    foreach ($content as $item) {
2460
      $output .= '<dt>' . l($item['title'], $item['href'], $item['localized_options']) . '</dt>';
2461
      if (!$compact && isset($item['description'])) {
2462
        $output .= '<dd>' . filter_xss_admin($item['description']) . '</dd>';
2463
      }
2464
    }
2465
    $output .= '</dl>';
2466
  }
2467
  return $output;
2468
}
2469

    
2470
/**
2471
 * Returns HTML for an administrative page.
2472
 *
2473
 * @param $variables
2474
 *   An associative array containing:
2475
 *   - blocks: An array of blocks to display. Each array should include a
2476
 *     'title', a 'description', a formatted 'content' and a 'position' which
2477
 *     will control which container it will be in. This is usually 'left' or
2478
 *     'right'.
2479
 *
2480
 * @ingroup themeable
2481
 */
2482
function theme_admin_page($variables) {
2483
  $blocks = $variables['blocks'];
2484

    
2485
  $stripe = 0;
2486
  $container = array();
2487

    
2488
  foreach ($blocks as $block) {
2489
    if ($block_output = theme('admin_block', array('block' => $block))) {
2490
      if (empty($block['position'])) {
2491
        // perform automatic striping.
2492
        $block['position'] = ++$stripe % 2 ? 'left' : 'right';
2493
      }
2494
      if (!isset($container[$block['position']])) {
2495
        $container[$block['position']] = '';
2496
      }
2497
      $container[$block['position']] .= $block_output;
2498
    }
2499
  }
2500

    
2501
  $output = '<div class="admin clearfix">';
2502
  $output .= theme('system_compact_link');
2503

    
2504
  foreach ($container as $id => $data) {
2505
    $output .= '<div class="' . $id . ' clearfix">';
2506
    $output .= $data;
2507
    $output .= '</div>';
2508
  }
2509
  $output .= '</div>';
2510
  return $output;
2511
}
2512

    
2513
/**
2514
 * Returns HTML for the output of the dashboard page.
2515
 *
2516
 * @param $variables
2517
 *   An associative array containing:
2518
 *   - menu_items: An array of modules to be displayed.
2519
 *
2520
 * @ingroup themeable
2521
 */
2522
function theme_system_admin_index($variables) {
2523
  $menu_items = $variables['menu_items'];
2524

    
2525
  $stripe = 0;
2526
  $container = array('left' => '', 'right' => '');
2527
  $flip = array('left' => 'right', 'right' => 'left');
2528
  $position = 'left';
2529

    
2530
  // Iterate over all modules.
2531
  foreach ($menu_items as $module => $block) {
2532
    list($description, $items) = $block;
2533

    
2534
    // Output links.
2535
    if (count($items)) {
2536
      $block = array();
2537
      $block['title'] = $module;
2538
      $block['content'] = theme('admin_block_content', array('content' => $items));
2539
      $block['description'] = t($description);
2540
      $block['show'] = TRUE;
2541

    
2542
      if ($block_output = theme('admin_block', array('block' => $block))) {
2543
        if (!isset($block['position'])) {
2544
          // Perform automatic striping.
2545
          $block['position'] = $position;
2546
          $position = $flip[$position];
2547
        }
2548
        $container[$block['position']] .= $block_output;
2549
      }
2550
    }
2551
  }
2552

    
2553
  $output = '<div class="admin clearfix">';
2554
  $output .= theme('system_compact_link');
2555
  foreach ($container as $id => $data) {
2556
    $output .= '<div class="' . $id . ' clearfix">';
2557
    $output .= $data;
2558
    $output .= '</div>';
2559
  }
2560
  $output .= '</div>';
2561

    
2562
  return $output;
2563
}
2564

    
2565
/**
2566
 * Returns HTML for the status report.
2567
 *
2568
 * @param $variables
2569
 *   An associative array containing:
2570
 *   - requirements: An array of requirements.
2571
 *
2572
 * @ingroup themeable
2573
 */
2574
function theme_status_report($variables) {
2575
  $requirements = $variables['requirements'];
2576
  $severities = array(
2577
    REQUIREMENT_INFO => array(
2578
      'title' => t('Info'),
2579
      'class' => 'info',
2580
    ),
2581
    REQUIREMENT_OK => array(
2582
      'title' => t('OK'),
2583
      'class' => 'ok',
2584
    ),
2585
    REQUIREMENT_WARNING => array(
2586
      'title' => t('Warning'),
2587
      'class' => 'warning',
2588
    ),
2589
    REQUIREMENT_ERROR => array(
2590
      'title' => t('Error'),
2591
      'class' => 'error',
2592
    ),
2593
  );
2594
  $output = '<table class="system-status-report">';
2595

    
2596
  foreach ($requirements as $requirement) {
2597
    if (empty($requirement['#type'])) {
2598
      $severity = $severities[isset($requirement['severity']) ? (int) $requirement['severity'] : REQUIREMENT_OK];
2599
      $severity['icon'] = '<div title="' . $severity['title'] . '"><span class="element-invisible">' . $severity['title'] . '</span></div>';
2600
      // The requirement's 'value' key is optional, provide a default value.
2601
      $requirement['value'] = isset($requirement['value']) ? $requirement['value'] : '';
2602

    
2603
      // Output table row(s)
2604
      if (!empty($requirement['description'])) {
2605
        $output .= '<tr class="' . $severity['class'] . ' merge-down"><td class="status-icon">' . $severity['icon'] . '</td><td class="status-title">' . $requirement['title'] . '</td><td class="status-value">' . $requirement['value'] . '</td></tr>';
2606
        $output .= '<tr class="' . $severity['class'] . ' merge-up"><td colspan="3" class="status-description">' . $requirement['description'] . '</td></tr>';
2607
      }
2608
      else {
2609
        $output .= '<tr class="' . $severity['class'] . '"><td class="status-icon">' . $severity['icon'] . '</td><td class="status-title">' . $requirement['title'] . '</td><td class="status-value">' . $requirement['value'] . '</td></tr>';
2610
      }
2611
    }
2612
  }
2613

    
2614
  $output .= '</table>';
2615
  return $output;
2616
}
2617

    
2618
/**
2619
 * Returns HTML for the modules form.
2620
 *
2621
 * @param $variables
2622
 *   An associative array containing:
2623
 *   - form: A render element representing the form.
2624
 *
2625
 * @ingroup themeable
2626
 */
2627
function theme_system_modules_fieldset($variables) {
2628
  $form = $variables['form'];
2629

    
2630
  // Individual table headers.
2631
  $rows = array();
2632
  // Iterate through all the modules, which are
2633
  // children of this fieldset.
2634
  foreach (element_children($form) as $key) {
2635
    // Stick it into $module for easier accessing.
2636
    $module = $form[$key];
2637
    $row = array();
2638
    unset($module['enable']['#title']);
2639
    $row[] = array('class' => array('checkbox'), 'data' => drupal_render($module['enable']));
2640
    $label = '<label';
2641
    if (isset($module['enable']['#id'])) {
2642
      $label .= ' for="' . $module['enable']['#id'] . '"';
2643
    }
2644
    $row[] = $label . '><strong>' . drupal_render($module['name']) . '</strong></label>';
2645
    $row[] = drupal_render($module['version']);
2646
    // Add the description, along with any modules it requires.
2647
    $description = drupal_render($module['description']);
2648
    if ($module['#requires']) {
2649
      $description .= '<div class="admin-requirements">' . t('Requires: !module-list', array('!module-list' => implode(', ', $module['#requires']))) . '</div>';
2650
    }
2651
    if ($module['#required_by']) {
2652
      $description .= '<div class="admin-requirements">' . t('Required by: !module-list', array('!module-list' => implode(', ', $module['#required_by']))) . '</div>';
2653
    }
2654
    $row[] = array('data' => $description, 'class' => array('description'));
2655
    // Display links (such as help or permissions) in their own columns.
2656
    foreach (array('help', 'permissions', 'configure') as $link_type) {
2657
      $row[] = array('data' => drupal_render($module['links'][$link_type]), 'class' => array($link_type));
2658
    }
2659
    $rows[] = $row;
2660
  }
2661

    
2662
  return theme('table', array('header' => $form['#header'], 'rows' => $rows));
2663
}
2664

    
2665
/**
2666
 * Returns HTML for a message about incompatible modules.
2667
 *
2668
 * @param $variables
2669
 *   An associative array containing:
2670
 *   - message: The form array representing the currently disabled modules.
2671
 *
2672
 * @ingroup themeable
2673
 */
2674
function theme_system_modules_incompatible($variables) {
2675
  return '<div class="incompatible">' . $variables['message'] . '</div>';
2676
}
2677

    
2678
/**
2679
 * Returns HTML for a table of currently disabled modules.
2680
 *
2681
 * @param $variables
2682
 *   An associative array containing:
2683
 *   - form: A render element representing the form.
2684
 *
2685
 * @ingroup themeable
2686
 */
2687
function theme_system_modules_uninstall($variables) {
2688
  $form = $variables['form'];
2689

    
2690
  // No theming for the confirm form.
2691
  if (isset($form['confirm'])) {
2692
    return drupal_render($form);
2693
  }
2694

    
2695
  // Table headers.
2696
  $header = array(t('Uninstall'),
2697
    t('Name'),
2698
    t('Description'),
2699
  );
2700

    
2701
  // Display table.
2702
  $rows = array();
2703
  foreach (element_children($form['modules']) as $module) {
2704
    if (!empty($form['modules'][$module]['#required_by'])) {
2705
      $disabled_message = format_plural(count($form['modules'][$module]['#required_by']),
2706
        'To uninstall @module, the following module must be uninstalled first: @required_modules',
2707
        'To uninstall @module, the following modules must be uninstalled first: @required_modules',
2708
        array('@module' => $form['modules'][$module]['#module_name'], '@required_modules' => implode(', ', $form['modules'][$module]['#required_by'])));
2709
      $disabled_message = '<div class="admin-requirements">' . $disabled_message . '</div>';
2710
    }
2711
    else {
2712
      $disabled_message = '';
2713
    }
2714
    $rows[] = array(
2715
      array('data' => drupal_render($form['uninstall'][$module]), 'align' => 'center'),
2716
      '<strong><label for="' . $form['uninstall'][$module]['#id'] . '">' . drupal_render($form['modules'][$module]['name']) . '</label></strong>',
2717
      array('data' => drupal_render($form['modules'][$module]['description']) . $disabled_message, 'class' => array('description')),
2718
    );
2719
  }
2720

    
2721
  $output  = theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No modules are available to uninstall.')));
2722
  $output .= drupal_render_children($form);
2723

    
2724
  return $output;
2725
}
2726

    
2727
/**
2728
 * Returns HTML for the Appearance page.
2729
 *
2730
 * @param $variables
2731
 *   An associative array containing:
2732
 *   - theme_groups: An associative array containing groups of themes.
2733
 *
2734
 * @ingroup themeable
2735
 */
2736
function theme_system_themes_page($variables) {
2737
  $theme_groups = $variables['theme_groups'];
2738

    
2739
  $output = '<div id="system-themes-page">';
2740

    
2741
  foreach ($variables['theme_group_titles'] as $state => $title) {
2742
    if (!count($theme_groups[$state])) {
2743
      // Skip this group of themes if no theme is there.
2744
      continue;
2745
    }
2746
    // Start new theme group.
2747
    $output .= '<div class="system-themes-list system-themes-list-'. $state .' clearfix"><h2>'. $title .'</h2>';
2748

    
2749
    foreach ($theme_groups[$state] as $theme) {
2750

    
2751
      // Theme the screenshot.
2752
      $screenshot = $theme->screenshot ? theme('image', $theme->screenshot) : '<div class="no-screenshot">' . t('no screenshot') . '</div>';
2753

    
2754
      // Localize the theme description.
2755
      $description = t($theme->info['description']);
2756

    
2757
      // Style theme info
2758
      $notes = count($theme->notes) ? ' (' . join(', ', $theme->notes) . ')' : '';
2759
      $theme->classes[] = 'theme-selector';
2760
      $theme->classes[] = 'clearfix';
2761
      $output .= '<div class="'. join(' ', $theme->classes) .'">' . $screenshot . '<div class="theme-info"><h3>' . $theme->info['name'] . ' ' . (isset($theme->info['version']) ? $theme->info['version'] : '') . $notes . '</h3><div class="theme-description">' . $description . '</div>';
2762

    
2763
      // Make sure to provide feedback on compatibility.
2764
      if (!empty($theme->incompatible_core)) {
2765
        $output .= '<div class="incompatible">' . t('This version is not compatible with Drupal !core_version and should be replaced.', array('!core_version' => DRUPAL_CORE_COMPATIBILITY)) . '</div>';
2766
      }
2767
      elseif (!empty($theme->incompatible_php)) {
2768
        if (substr_count($theme->info['php'], '.') < 2) {
2769
          $theme->info['php'] .= '.*';
2770
        }
2771
        $output .= '<div class="incompatible">' . t('This theme requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $theme->info['php'], '!php_version' => phpversion())) . '</div>';
2772
      }
2773
      else {
2774
        $output .= theme('links', array('links' => $theme->operations, 'attributes' => array('class' => array('operations', 'clearfix'))));
2775
      }
2776
      $output .= '</div></div>';
2777
    }
2778
    $output .= '</div>';
2779
  }
2780
  $output .= '</div>';
2781

    
2782
  return $output;
2783
}
2784

    
2785
/**
2786
 * Menu callback; present a form for deleting a date format.
2787
 */
2788
function system_date_delete_format_form($form, &$form_state, $dfid) {
2789
  $form['dfid'] = array(
2790
    '#type' => 'value',
2791
    '#value' => $dfid,
2792
  );
2793
  $format = system_get_date_format($dfid);
2794

    
2795
  $output = confirm_form($form,
2796
    t('Are you sure you want to remove the format %format?', array('%format' => format_date(REQUEST_TIME, 'custom', $format->format))),
2797
    'admin/config/regional/date-time/formats',
2798
    t('This action cannot be undone.'),
2799
    t('Remove'), t('Cancel'),
2800
    'confirm'
2801
  );
2802

    
2803
  return $output;
2804
}
2805

    
2806
/**
2807
 * Delete a configured date format.
2808
 */
2809
function system_date_delete_format_form_submit($form, &$form_state) {
2810
  if ($form_state['values']['confirm']) {
2811
    $format = system_get_date_format($form_state['values']['dfid']);
2812
    system_date_format_delete($form_state['values']['dfid']);
2813
    drupal_set_message(t('Removed date format %format.', array('%format' => format_date(REQUEST_TIME, 'custom', $format->format))));
2814
    $form_state['redirect'] = 'admin/config/regional/date-time/formats';
2815
  }
2816
}
2817

    
2818
/**
2819
 * Menu callback; present a form for deleting a date type.
2820
 */
2821
function system_delete_date_format_type_form($form, &$form_state, $format_type) {
2822
  $form['format_type'] = array(
2823
    '#type' => 'value',
2824
    '#value' => $format_type,
2825
  );
2826
  $type_info = system_get_date_types($format_type);
2827

    
2828
  $output = confirm_form($form,
2829
    t('Are you sure you want to remove the date type %type?', array('%type' => $type_info['title'])),
2830
    'admin/config/regional/date-time',
2831
    t('This action cannot be undone.'),
2832
    t('Remove'), t('Cancel'),
2833
    'confirm'
2834
  );
2835

    
2836
  return $output;
2837
}
2838

    
2839
/**
2840
 * Delete a configured date type.
2841
 */
2842
function system_delete_date_format_type_form_submit($form, &$form_state) {
2843
  if ($form_state['values']['confirm']) {
2844
    $type_info = system_get_date_types($form_state['values']['format_type']);
2845
    system_date_format_type_delete($form_state['values']['format_type']);
2846
    drupal_set_message(t('Removed date type %type.', array('%type' => $type_info['title'])));
2847
    $form_state['redirect'] = 'admin/config/regional/date-time';
2848
  }
2849
}
2850

    
2851

    
2852
/**
2853
 * Displays the date format strings overview page.
2854
 */
2855
function system_date_time_formats() {
2856
  $header = array(t('Format'), array('data' => t('Operations'), 'colspan' => '2'));
2857
  $rows = array();
2858

    
2859
  drupal_static_reset('system_get_date_formats');
2860
  $formats = system_get_date_formats('custom');
2861
  if (!empty($formats)) {
2862
    foreach ($formats as $format) {
2863
      $row = array();
2864
      $row[] = array('data' => format_date(REQUEST_TIME, 'custom', $format['format']));
2865
      $row[] = array('data' => l(t('edit'), 'admin/config/regional/date-time/formats/' . $format['dfid'] . '/edit'));
2866
      $row[] = array('data' => l(t('delete'), 'admin/config/regional/date-time/formats/' . $format['dfid'] . '/delete'));
2867
      $rows[] = $row;
2868
    }
2869
  }
2870

    
2871
  $build['date_formats_table'] = array(
2872
    '#theme' => 'table',
2873
    '#header' => $header,
2874
    '#rows' => $rows,
2875
    '#empty' => t('No custom date formats available. <a href="@link">Add date format</a>.', array('@link' => url('admin/config/regional/date-time/formats/add'))),
2876
  );
2877

    
2878
  return $build;
2879
}
2880

    
2881
/**
2882
 * Allow users to add additional date formats.
2883
 */
2884
function system_configure_date_formats_form($form, &$form_state, $dfid = 0) {
2885
  $ajax_path = 'admin/config/regional/date-time/formats/lookup';
2886
  $js_settings = array(
2887
    'type' => 'setting',
2888
    'data' => array(
2889
      'dateTime' => array(
2890
        'date-format' => array(
2891
          'text' => t('Displayed as'),
2892
          'lookup' => url($ajax_path, array('query' => array('token' => drupal_get_token($ajax_path)))),
2893
        ),
2894
      ),
2895
    ),
2896
  );
2897

    
2898
  if ($dfid) {
2899
    $form['dfid'] = array(
2900
      '#type' => 'value',
2901
      '#value' => $dfid,
2902
    );
2903
    $format = system_get_date_format($dfid);
2904
  }
2905

    
2906
  $now = ($dfid ? t('Displayed as %date', array('%date' => format_date(REQUEST_TIME, 'custom', $format->format))) : '');
2907

    
2908
  $form['date_format'] = array(
2909
    '#type' => 'textfield',
2910
    '#title' => t('Format string'),
2911
    '#maxlength' => 100,
2912
    '#description' => t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://php.net/manual/function.date.php')),
2913
    '#default_value' => ($dfid ? $format->format : ''),
2914
    '#field_suffix' => ' <small id="edit-date-format-suffix">' . $now . '</small>',
2915
    '#attached' => array(
2916
      'js' => array(drupal_get_path('module', 'system') . '/system.js', $js_settings),
2917
    ),
2918
    '#required' => TRUE,
2919
  );
2920

    
2921
  $form['actions'] = array('#type' => 'actions');
2922
  $form['actions']['update'] = array(
2923
    '#type' => 'submit',
2924
    '#value' => ($dfid ? t('Save format') : t('Add format')),
2925
  );
2926

    
2927
  $form['#validate'][] = 'system_add_date_formats_form_validate';
2928
  $form['#submit'][] = 'system_add_date_formats_form_submit';
2929

    
2930
  return $form;
2931
}
2932

    
2933
/**
2934
 * Validate new date format string submission.
2935
 */
2936
function system_add_date_formats_form_validate($form, &$form_state) {
2937
  $formats = system_get_date_formats('custom');
2938
  $format = trim($form_state['values']['date_format']);
2939
  if (!empty($formats) && in_array($format, array_keys($formats)) && (!isset($form_state['values']['dfid']) || $form_state['values']['dfid'] != $formats[$format]['dfid'])) {
2940
    form_set_error('date_format', t('This format already exists. Enter a unique format string.'));
2941
  }
2942
}
2943

    
2944
/**
2945
 * Process new date format string submission.
2946
 */
2947
function system_add_date_formats_form_submit($form, &$form_state) {
2948
  $format = array();
2949
  $format['format'] = trim($form_state['values']['date_format']);
2950
  $format['type'] = 'custom';
2951
  $format['locked'] = 0;
2952
  if (!empty($form_state['values']['dfid'])) {
2953
    system_date_format_save($format, $form_state['values']['dfid']);
2954
    drupal_set_message(t('Custom date format updated.'));
2955
  }
2956
  else {
2957
    $format['is_new'] = 1;
2958
    system_date_format_save($format);
2959
    drupal_set_message(t('Custom date format added.'));
2960
  }
2961

    
2962
  $form_state['redirect'] = 'admin/config/regional/date-time/formats';
2963
}
2964

    
2965
/**
2966
 * Menu callback; Displays an overview of available and configured actions.
2967
 */
2968
function system_actions_manage() {
2969
  actions_synchronize();
2970
  $actions = actions_list();
2971
  $actions_map = actions_actions_map($actions);
2972
  $options = array();
2973
  $unconfigurable = array();
2974

    
2975
  foreach ($actions_map as $key => $array) {
2976
    if ($array['configurable']) {
2977
      $options[$key] = $array['label'] . '...';
2978
    }
2979
    else {
2980
      $unconfigurable[] = $array;
2981
    }
2982
  }
2983

    
2984
  $row = array();
2985
  $instances_present = db_query("SELECT aid FROM {actions} WHERE parameters <> ''")->fetchField();
2986
  $header = array(
2987
    array('data' => t('Action type'), 'field' => 'type'),
2988
    array('data' => t('Label'), 'field' => 'label'),
2989
    array('data' => $instances_present ? t('Operations') : '', 'colspan' => '2')
2990
  );
2991
  $query = db_select('actions')->extend('PagerDefault')->extend('TableSort');
2992
  $result = $query
2993
    ->fields('actions')
2994
    ->limit(50)
2995
    ->orderByHeader($header)
2996
    ->execute();
2997

    
2998
  foreach ($result as $action) {
2999
    $row[] = array(
3000
      array('data' => $action->type),
3001
      array('data' => check_plain($action->label)),
3002
      array('data' => $action->parameters ? l(t('configure'), "admin/config/system/actions/configure/$action->aid") : ''),
3003
      array('data' => $action->parameters ? l(t('delete'), "admin/config/system/actions/delete/$action->aid") : '')
3004
    );
3005
  }
3006

    
3007
  if ($row) {
3008
    $pager = theme('pager');
3009
    if (!empty($pager)) {
3010
      $row[] = array(array('data' => $pager, 'colspan' => '3'));
3011
    }
3012
    $build['system_actions_header'] = array('#markup' => '<h3>' . t('Available actions:') . '</h3>');
3013
    $build['system_actions_table'] = array('#markup' => theme('table', array('header' => $header, 'rows' => $row)));
3014
  }
3015

    
3016
  if ($actions_map) {
3017
    $build['system_actions_manage_form'] = drupal_get_form('system_actions_manage_form', $options);
3018
  }
3019

    
3020
  return $build;
3021
}
3022

    
3023
/**
3024
 * Define the form for the actions overview page.
3025
 *
3026
 * @param $form_state
3027
 *   An associative array containing the current state of the form; not used.
3028
 * @param $options
3029
 *   An array of configurable actions.
3030
 * @return
3031
 *   Form definition.
3032
 *
3033
 * @ingroup forms
3034
 * @see system_actions_manage_form_submit()
3035
 */
3036
function system_actions_manage_form($form, &$form_state, $options = array()) {
3037
  $form['parent'] = array(
3038
    '#type' => 'fieldset',
3039
    '#title' => t('Create an advanced action'),
3040
    '#attributes' => array('class' => array('container-inline')),
3041
  );
3042
  $form['parent']['action'] = array(
3043
    '#type' => 'select',
3044
    '#title' => t('Action'),
3045
    '#title_display' => 'invisible',
3046
    '#options' => $options,
3047
    '#empty_option' => t('Choose an advanced action'),
3048
  );
3049
  $form['parent']['actions'] = array('#type' => 'actions');
3050
  $form['parent']['actions']['submit'] = array(
3051
    '#type' => 'submit',
3052
    '#value' => t('Create'),
3053
  );
3054
  return $form;
3055
}
3056

    
3057
/**
3058
 * Process system_actions_manage form submissions.
3059
 *
3060
 * @see system_actions_manage_form()
3061
 */
3062
function system_actions_manage_form_submit($form, &$form_state) {
3063
  if ($form_state['values']['action']) {
3064
    $form_state['redirect'] = 'admin/config/system/actions/configure/' . $form_state['values']['action'];
3065
  }
3066
}
3067

    
3068
/**
3069
 * Menu callback; Creates the form for configuration of a single action.
3070
 *
3071
 * We provide the "Description" field. The rest of the form is provided by the
3072
 * action. We then provide the Save button. Because we are combining unknown
3073
 * form elements with the action configuration form, we use an 'actions_' prefix
3074
 * on our elements.
3075
 *
3076
 * @param $action
3077
 *   Hash of an action ID or an integer. If it is a hash, we are
3078
 *   creating a new instance. If it is an integer, we are editing an existing
3079
 *   instance.
3080
 * @return
3081
 *   A form definition.
3082
 *
3083
 * @see system_actions_configure_validate()
3084
 * @see system_actions_configure_submit()
3085
 */
3086
function system_actions_configure($form, &$form_state, $action = NULL) {
3087
  if ($action === NULL) {
3088
    drupal_goto('admin/config/system/actions');
3089
  }
3090

    
3091
  $actions_map = actions_actions_map(actions_list());
3092
  $edit = array();
3093

    
3094
  // Numeric action denotes saved instance of a configurable action.
3095
  if (is_numeric($action)) {
3096
    $aid = $action;
3097
    // Load stored parameter values from database.
3098
    $data = db_query("SELECT * FROM {actions} WHERE aid = :aid", array(':aid' => $aid))->fetch();
3099
    $edit['actions_label'] = $data->label;
3100
    $edit['actions_type'] = $data->type;
3101
    $function = $data->callback;
3102
    $action = drupal_hash_base64($data->callback);
3103
    $params = unserialize($data->parameters);
3104
    if ($params) {
3105
      foreach ($params as $name => $val) {
3106
        $edit[$name] = $val;
3107
      }
3108
    }
3109
  }
3110
  // Otherwise, we are creating a new action instance.
3111
  else {
3112
    $function = $actions_map[$action]['callback'];
3113
    $edit['actions_label'] = $actions_map[$action]['label'];
3114
    $edit['actions_type'] = $actions_map[$action]['type'];
3115
  }
3116

    
3117
  $form['actions_label'] = array(
3118
    '#type' => 'textfield',
3119
    '#title' => t('Label'),
3120
    '#default_value' => $edit['actions_label'],
3121
    '#maxlength' => '255',
3122
    '#description' => t('A unique label for this advanced action. This label will be displayed in the interface of modules that integrate with actions, such as Trigger module.'),
3123
    '#weight' => -10
3124
  );
3125
  $action_form = $function . '_form';
3126
  $form = array_merge($form, $action_form($edit));
3127
  $form['actions_type'] = array(
3128
    '#type' => 'value',
3129
    '#value' => $edit['actions_type'],
3130
  );
3131
  $form['actions_action'] = array(
3132
    '#type' => 'hidden',
3133
    '#value' => $action,
3134
  );
3135
  // $aid is set when configuring an existing action instance.
3136
  if (isset($aid)) {
3137
    $form['actions_aid'] = array(
3138
      '#type' => 'hidden',
3139
      '#value' => $aid,
3140
    );
3141
  }
3142
  $form['actions_configured'] = array(
3143
    '#type' => 'hidden',
3144
    '#value' => '1',
3145
  );
3146
  $form['actions'] = array('#type' => 'actions');
3147
  $form['actions']['submit'] = array(
3148
    '#type' => 'submit',
3149
    '#value' => t('Save'),
3150
    '#weight' => 13
3151
  );
3152

    
3153
  return $form;
3154
}
3155

    
3156
/**
3157
 * Validate system_actions_configure() form submissions.
3158
 */
3159
function system_actions_configure_validate($form, &$form_state) {
3160
  $function = actions_function_lookup($form_state['values']['actions_action']) . '_validate';
3161
  // Hand off validation to the action.
3162
  if (function_exists($function)) {
3163
    $function($form, $form_state);
3164
  }
3165
}
3166

    
3167
/**
3168
 * Process system_actions_configure() form submissions.
3169
 */
3170
function system_actions_configure_submit($form, &$form_state) {
3171
  $function = actions_function_lookup($form_state['values']['actions_action']);
3172
  $submit_function = $function . '_submit';
3173

    
3174
  // Action will return keyed array of values to store.
3175
  $params = $submit_function($form, $form_state);
3176
  $aid = isset($form_state['values']['actions_aid']) ? $form_state['values']['actions_aid'] : NULL;
3177

    
3178
  actions_save($function, $form_state['values']['actions_type'], $params, $form_state['values']['actions_label'], $aid);
3179
  drupal_set_message(t('The action has been successfully saved.'));
3180

    
3181
  $form_state['redirect'] = 'admin/config/system/actions/manage';
3182
}
3183

    
3184
/**
3185
 * Create the form for confirmation of deleting an action.
3186
 *
3187
 * @see system_actions_delete_form_submit()
3188
 * @ingroup forms
3189
 */
3190
function system_actions_delete_form($form, &$form_state, $action) {
3191
  $form['aid'] = array(
3192
    '#type' => 'hidden',
3193
    '#value' => $action->aid,
3194
  );
3195
  return confirm_form($form,
3196
    t('Are you sure you want to delete the action %action?', array('%action' => $action->label)),
3197
    'admin/config/system/actions/manage',
3198
    t('This cannot be undone.'),
3199
    t('Delete'),
3200
    t('Cancel')
3201
  );
3202
}
3203

    
3204
/**
3205
 * Process system_actions_delete form submissions.
3206
 *
3207
 * Post-deletion operations for action deletion.
3208
 */
3209
function system_actions_delete_form_submit($form, &$form_state) {
3210
  $aid = $form_state['values']['aid'];
3211
  $action = actions_load($aid);
3212
  actions_delete($aid);
3213
  watchdog('user', 'Deleted action %aid (%action)', array('%aid' => $aid, '%action' => $action->label));
3214
  drupal_set_message(t('Action %action was deleted', array('%action' => $action->label)));
3215
  $form_state['redirect'] = 'admin/config/system/actions/manage';
3216
}
3217

    
3218
/**
3219
 * Post-deletion operations for deleting action orphans.
3220
 *
3221
 * @param $orphaned
3222
 *   An array of orphaned actions.
3223
 */
3224
function system_action_delete_orphans_post($orphaned) {
3225
  foreach ($orphaned as $callback) {
3226
    drupal_set_message(t("Deleted orphaned action (%action).", array('%action' => $callback)));
3227
  }
3228
}
3229

    
3230
/**
3231
 * Remove actions that are in the database but not supported by any enabled module.
3232
 */
3233
function system_actions_remove_orphans() {
3234
  actions_synchronize(TRUE);
3235
  drupal_goto('admin/config/system/actions/manage');
3236
}