Projet

Général

Profil

Paste
Télécharger (113 ko) Statistiques
| Branche: | Révision:

root / drupal7 / modules / system / system.admin.inc @ 4444412d

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 ($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 ($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 ($file = $values['logo_upload']) {
707
    unset($values['logo_upload']);
708
    $filename = file_unmanaged_copy($file->uri);
709
    $values['default_logo'] = 0;
710
    $values['logo_path'] = $filename;
711
    $values['toggle_logo'] = 1;
712
  }
713
  if ($file = $values['favicon_upload']) {
714
    unset($values['favicon_upload']);
715
    $filename = file_unmanaged_copy($file->uri);
716
    $values['default_favicon'] = 0;
717
    $values['favicon_path'] = $filename;
718
    $values['toggle_favicon'] = 1;
719
  }
720

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

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

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

    
737
  cache_clear_all();
738
}
739

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

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

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

    
800
  uasort($visible_files, 'system_sort_modules_by_info_name');
801

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

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

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

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

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

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

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

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

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

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

    
942
  return $form;
943
}
944

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

    
952
/**
953
 * Array sorting callback; sorts modules or themes by their name.
954
 */
955
function system_sort_themes($a, $b) {
956
  if ($a->is_default) {
957
    return -1;
958
  }
959
  if ($b->is_default) {
960
    return 1;
961
  }
962
  return strcasecmp($a->info['name'], $b->info['name']);
963
}
964

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

    
993
  // Check the compatibilities.
994
  $compatible = TRUE;
995
  $status_short = '';
996
  $status_long = '';
997

    
998
  // Initialize empty arrays of long and short reasons explaining why the
999
  // module is incompatible.
1000
  // Add each reason as a separate element in both the arrays.
1001
  $reasons_short = array();
1002
  $reasons_long = array();
1003

    
1004
  // Check the core compatibility.
1005
  if (!isset($info['core']) || $info['core'] != DRUPAL_CORE_COMPATIBILITY) {
1006
    $compatible = FALSE;
1007
    $reasons_short[] = t('Incompatible with this version of Drupal core.');
1008
    $reasons_long[] = t('This version is not compatible with Drupal !core_version and should be replaced.', array('!core_version' => DRUPAL_CORE_COMPATIBILITY));
1009
  }
1010

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

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

    
1043
  // Build operation links.
1044
  foreach (array('help', 'permissions', 'configure') as $key) {
1045
    $form['links'][$key] = (isset($extra['links'][$key]) ? $extra['links'][$key] : array());
1046
  }
1047

    
1048
  return $form;
1049
}
1050

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

    
1065
  $form['validation_modules'] = array('#type' => 'value', '#value' => $modules);
1066
  $form['status']['#tree'] = TRUE;
1067

    
1068
  foreach ($storage['more_required'] as $info) {
1069
    $t_argument = array(
1070
      '@module' => $info['name'],
1071
      '@required' => implode(', ', $info['requires']),
1072
    );
1073
    $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);
1074
  }
1075

    
1076
  foreach ($storage['missing_modules'] as $name => $info) {
1077
    $t_argument = array(
1078
      '@module' => $name,
1079
      '@depends' => implode(', ', $info['depends']),
1080
    );
1081
    $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);
1082
  }
1083

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

    
1086
  if ($form) {
1087
    // Set some default form values
1088
    $form = confirm_form(
1089
      $form,
1090
      t('Some required modules must be enabled'),
1091
      'admin/modules',
1092
      t('Would you like to continue with the above?'),
1093
      t('Continue'),
1094
      t('Cancel'));
1095
    return $form;
1096
  }
1097
}
1098

    
1099
/**
1100
 * Submit callback; handles modules form submission.
1101
 */
1102
function system_modules_submit($form, &$form_state) {
1103
  include_once DRUPAL_ROOT . '/includes/install.inc';
1104

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

    
1122
  // Collect data for all modules to be able to determine dependencies.
1123
  $files = system_rebuild_module_data();
1124

    
1125
  // Sorts modules by weight.
1126
  $sort = array();
1127
  foreach (array_keys($modules) as $module) {
1128
    $sort[$module] = $files[$module]->sort;
1129
  }
1130
  array_multisort($sort, $modules);
1131

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

    
1154
      // Stores additional modules that need to be enabled in $more_required.
1155
      if (!empty($dependencies)) {
1156
        $more_required[$name] = array(
1157
          'name' => $files[$name]->info['name'],
1158
          'requires' => $dependencies,
1159
        );
1160
      }
1161
    }
1162
  }
1163

    
1164
  // Redirects to confirmation form if more modules need to be enabled.
1165
  if ((!empty($more_required) || !empty($missing_modules)) && !isset($form_state['values']['confirm'])) {
1166
    $form_state['storage'] = array(
1167
      'more_required' => $more_required,
1168
      'modules' => $modules,
1169
      'missing_modules' => $missing_modules,
1170
    );
1171
    $form_state['rebuild'] = TRUE;
1172
    return;
1173
  }
1174

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

    
1189
  // Initializes array of actions.
1190
  $actions = array(
1191
    'enable' => array(),
1192
    'disable' => array(),
1193
    'install' => array(),
1194
  );
1195

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

    
1212
  // Gets list of modules prior to install process, unsets $form_state['storage']
1213
  // so we don't get redirected back to the confirmation form.
1214
  $pre_install_list = module_list();
1215
  unset($form_state['storage']);
1216

    
1217
  // Reverse the 'enable' list, to order dependencies before dependents.
1218
  krsort($actions['enable']);
1219

    
1220
  // Installs, enables, and disables modules.
1221
  module_enable($actions['enable'], FALSE);
1222
  module_disable($actions['disable'], FALSE);
1223

    
1224
  // Gets module list after install process, flushes caches and displays a
1225
  // message if there are changes.
1226
  $post_install_list = module_list(TRUE);
1227
  if ($pre_install_list != $post_install_list) {
1228
    drupal_flush_all_caches();
1229
    drupal_set_message(t('The configuration options have been saved.'));
1230
  }
1231

    
1232
  $form_state['redirect'] = 'admin/modules';
1233
}
1234

    
1235
/**
1236
 * Uninstall functions
1237
 */
1238

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

    
1254
  // Display the confirm form if any modules have been submitted.
1255
  if (!empty($form_state['storage']) && $confirm_form = system_modules_uninstall_confirm_form($form_state['storage'])) {
1256
    return $confirm_form;
1257
  }
1258

    
1259
  // Get a list of disabled, installed modules.
1260
  $all_modules = system_rebuild_module_data();
1261
  $disabled_modules = array();
1262
  foreach ($all_modules as $name => $module) {
1263
    if (empty($module->status) && $module->schema_version > SCHEMA_UNINSTALLED) {
1264
      $disabled_modules[$name] = $module;
1265
    }
1266
  }
1267

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

    
1306
  return $form;
1307
}
1308

    
1309
/**
1310
 * Confirm uninstall of selected modules.
1311
 *
1312
 * @ingroup forms
1313
 * @param $storage
1314
 *   An associative array of modules selected to be uninstalled.
1315
 * @return
1316
 *   A form array representing modules to confirm.
1317
 */
1318
function system_modules_uninstall_confirm_form($storage) {
1319
  // Nothing to build.
1320
  if (empty($storage)) {
1321
    return;
1322
  }
1323

    
1324
  // Construct the hidden form elements and list items.
1325
  foreach (array_filter($storage['uninstall']) as $module => $value) {
1326
    $info = drupal_parse_info_file(drupal_get_path('module', $module) . '/' . $module . '.info');
1327
    $uninstall[] = $info['name'];
1328
    $form['uninstall'][$module] = array('#type' => 'hidden',
1329
      '#value' => 1,
1330
    );
1331
  }
1332

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

    
1349
/**
1350
 * Validates the submitted uninstall form.
1351
 */
1352
function system_modules_uninstall_validate($form, &$form_state) {
1353
  // Form submitted, but no modules selected.
1354
  if (!count(array_filter($form_state['values']['uninstall']))) {
1355
    drupal_set_message(t('No modules selected.'), 'error');
1356
    drupal_goto('admin/modules/uninstall');
1357
  }
1358
}
1359

    
1360
/**
1361
 * Processes the submitted uninstall form.
1362
 */
1363
function system_modules_uninstall_submit($form, &$form_state) {
1364
  // Make sure the install API is available.
1365
  include_once DRUPAL_ROOT . '/includes/install.inc';
1366

    
1367
  if (!empty($form['#confirmed'])) {
1368
    // Call the uninstall routine for each selected module.
1369
    $modules = array_keys($form_state['values']['uninstall']);
1370
    drupal_uninstall_modules($modules);
1371
    drupal_set_message(t('The selected modules have been uninstalled.'));
1372

    
1373
    $form_state['redirect'] = 'admin/modules/uninstall';
1374
  }
1375
  else {
1376
    $form_state['storage'] = $form_state['values'];
1377
    $form_state['rebuild'] = TRUE;
1378
  }
1379
}
1380

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

    
1399
  $build['system_ip_blocking_form'] = drupal_get_form('system_ip_blocking_form', $default_ip);
1400

    
1401
  $build['system_ip_blocking_table'] = array(
1402
    '#theme' => 'table',
1403
    '#header' => $header,
1404
    '#rows' => $rows,
1405
    '#empty' => t('No blocked IP addresses available.'),
1406
  );
1407

    
1408
  return $build;
1409
}
1410

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

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

    
1450
function system_ip_blocking_form_submit($form, &$form_state) {
1451
  $ip = trim($form_state['values']['ip']);
1452
  db_insert('blocked_ips')
1453
    ->fields(array('ip' => $ip))
1454
    ->execute();
1455
  drupal_set_message(t('The IP address %ip has been blocked.', array('%ip' => $ip)));
1456
  $form_state['redirect'] = 'admin/config/people/ip-blocking';
1457
  return;
1458
}
1459

    
1460
/**
1461
 * IP deletion confirm page.
1462
 *
1463
 * @see system_ip_blocking_delete_submit()
1464
 */
1465
function system_ip_blocking_delete($form, &$form_state, $iid) {
1466
  $form['blocked_ip'] = array(
1467
    '#type' => 'value',
1468
    '#value' => $iid,
1469
  );
1470
  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'));
1471
}
1472

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

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

    
1555
  $form['#validate'][] = 'system_site_information_settings_validate';
1556

    
1557
  return system_settings_form($form);
1558
}
1559

    
1560
/**
1561
 * Validates the submitted site-information form.
1562
 */
1563
function system_site_information_settings_validate($form, &$form_state) {
1564
  // Validate the e-mail address.
1565
  if ($error = user_validate_mail($form_state['values']['site_mail'])) {
1566
    form_set_error('site_mail', $error);
1567
  }
1568
  // Check for empty front page path.
1569
  if (empty($form_state['values']['site_frontpage'])) {
1570
    // Set to default "node".
1571
    form_set_value($form['front_page']['site_frontpage'], 'node', $form_state);
1572
  }
1573
  else {
1574
    // Get the normal path of the front page.
1575
    form_set_value($form['front_page']['site_frontpage'], drupal_get_normal_path($form_state['values']['site_frontpage']), $form_state);
1576
  }
1577
  // Validate front page path.
1578
  if (!drupal_valid_path($form_state['values']['site_frontpage'])) {
1579
    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'])));
1580
  }
1581
  // Get the normal paths of both error pages.
1582
  if (!empty($form_state['values']['site_403'])) {
1583
    form_set_value($form['error_page']['site_403'], drupal_get_normal_path($form_state['values']['site_403']), $form_state);
1584
  }
1585
  if (!empty($form_state['values']['site_404'])) {
1586
    form_set_value($form['error_page']['site_404'], drupal_get_normal_path($form_state['values']['site_404']), $form_state);
1587
  }
1588
  // Validate 403 error path.
1589
  if (!empty($form_state['values']['site_403']) && !drupal_valid_path($form_state['values']['site_403'])) {
1590
    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'])));
1591
  }
1592
  // Validate 404 error path.
1593
  if (!empty($form_state['values']['site_404']) && !drupal_valid_path($form_state['values']['site_404'])) {
1594
    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'])));
1595
  }
1596
}
1597

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

    
1619
  $form['cron_url'] = array(
1620
    '#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>',
1621
  );
1622

    
1623
  $form['cron'] = array(
1624
    '#type' => 'fieldset',
1625
  );
1626
  $form['cron']['cron_safe_threshold'] = array(
1627
    '#type' => 'select',
1628
    '#title' => t('Run cron every'),
1629
    '#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'))),
1630
    '#default_value' => variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD),
1631
    '#options' => array(0 => t('Never')) + drupal_map_assoc(array(3600, 10800, 21600, 43200, 86400, 604800), 'format_interval'),
1632
  );
1633

    
1634
  return system_settings_form($form);
1635
}
1636

    
1637
/**
1638
 * Submit callback; run cron.
1639
 *
1640
 * @ingroup forms
1641
 */
1642
function system_run_cron_submit($form, &$form_state) {
1643
  // Run cron manually from Cron form.
1644
  if (drupal_cron_run()) {
1645
    drupal_set_message(t('Cron run successfully.'));
1646
  }
1647
  else {
1648
    drupal_set_message(t('Cron run failed.'), 'error');
1649
  }
1650

    
1651
  drupal_goto('admin/config/system/cron');
1652
}
1653

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

    
1673
  return system_settings_form($form);
1674
}
1675

    
1676
/**
1677
 * Form builder; Configure site performance settings.
1678
 *
1679
 * @ingroup forms
1680
 * @see system_settings_form()
1681
 */
1682
function system_performance_settings() {
1683
  drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
1684

    
1685
  $form['clear_cache'] = array(
1686
    '#type' => 'fieldset',
1687
    '#title' => t('Clear cache'),
1688
  );
1689

    
1690
  $form['clear_cache']['clear'] = array(
1691
    '#type' => 'submit',
1692
    '#value' => t('Clear all caches'),
1693
    '#submit' => array('system_clear_cache_submit'),
1694
  );
1695

    
1696
  $form['caching'] = array(
1697
    '#type' => 'fieldset',
1698
    '#title' => t('Caching'),
1699
  );
1700

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

    
1725
  $directory = 'public://';
1726
  $is_writable = is_dir($directory) && is_writable($directory);
1727
  $disabled = !$is_writable;
1728
  $disabled_message = '';
1729
  if (!$is_writable) {
1730
    $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')));
1731
  }
1732

    
1733
  $form['bandwidth_optimization'] = array(
1734
    '#type' => 'fieldset',
1735
    '#title' => t('Bandwidth optimization'),
1736
    '#description' => t('External resources can be optimized automatically, which can reduce both the size and number of requests made to your website.') . $disabled_message,
1737
  );
1738

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

    
1760
  $form['#submit'][] = 'drupal_clear_css_cache';
1761
  $form['#submit'][] = 'drupal_clear_js_cache';
1762
  // This form allows page compression settings to be changed, which can
1763
  // invalidate the page cache, so it needs to be cleared on form submit.
1764
  $form['#submit'][] = 'system_clear_page_cache_submit';
1765

    
1766
  return system_settings_form($form);
1767
}
1768

    
1769
/**
1770
 * Submit callback; clear system caches.
1771
 *
1772
 * @ingroup forms
1773
 */
1774
function system_clear_cache_submit($form, &$form_state) {
1775
  drupal_flush_all_caches();
1776
  drupal_set_message(t('Caches cleared.'));
1777
}
1778

    
1779
/**
1780
 * Submit callback; clear the page cache.
1781
 *
1782
 * @ingroup forms
1783
 */
1784
function system_clear_page_cache_submit($form, &$form_state) {
1785
  cache_clear_all('*', 'cache_page', TRUE);
1786
}
1787

    
1788
/**
1789
 * Form builder; Configure the site file handling.
1790
 *
1791
 * @ingroup forms
1792
 * @see system_settings_form()
1793
 */
1794
function system_file_system_settings() {
1795
  $form['file_public_path'] = array(
1796
    '#type' => 'textfield',
1797
    '#title' => t('Public file system path'),
1798
    '#default_value' => variable_get('file_public_path', conf_path() . '/files'),
1799
    '#maxlength' => 255,
1800
    '#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.'),
1801
    '#after_build' => array('system_check_directory'),
1802
  );
1803

    
1804
  $form['file_private_path'] = array(
1805
    '#type' => 'textfield',
1806
    '#title' => t('Private file system path'),
1807
    '#default_value' => variable_get('file_private_path', ''),
1808
    '#maxlength' => 255,
1809
    '#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')),
1810
    '#after_build' => array('system_check_directory'),
1811
  );
1812

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

    
1827
  if (!empty($options)) {
1828
    $form['file_default_scheme'] = array(
1829
      '#type' => 'radios',
1830
      '#title' => t('Default download method'),
1831
      '#default_value' => variable_get('file_default_scheme', isset($options['public']) ? 'public' : key($options)),
1832
      '#options' => $options,
1833
      '#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.'),
1834
    );
1835
  }
1836

    
1837
  return system_settings_form($form);
1838
}
1839

    
1840
/**
1841
 * Form builder; Configure site image toolkit usage.
1842
 *
1843
 * @ingroup forms
1844
 * @see system_settings_form()
1845
 */
1846
function system_image_toolkit_settings() {
1847
  $toolkits_available = image_get_available_toolkits();
1848
  $current_toolkit = image_get_toolkit();
1849

    
1850
  if (count($toolkits_available) == 0) {
1851
    variable_del('image_toolkit');
1852
    $form['image_toolkit_help'] = array(
1853
      '#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'))),
1854
    );
1855
    return $form;
1856
  }
1857

    
1858
  if (count($toolkits_available) > 1) {
1859
    $form['image_toolkit'] = array(
1860
      '#type' => 'radios',
1861
      '#title' => t('Select an image processing toolkit'),
1862
      '#default_value' => variable_get('image_toolkit', $current_toolkit),
1863
      '#options' => $toolkits_available
1864
    );
1865
  }
1866
  else {
1867
    variable_set('image_toolkit', key($toolkits_available));
1868
  }
1869

    
1870
  // Get the toolkit's settings form.
1871
  $function = 'image_' . $current_toolkit . '_settings';
1872
  if (function_exists($function)) {
1873
    $form['image_toolkit_settings'] = $function();
1874
  }
1875

    
1876
  return system_settings_form($form);
1877
}
1878

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

    
1907
  return system_settings_form($form);
1908
}
1909

    
1910
/**
1911
 * Form builder; Configure the site regional settings.
1912
 *
1913
 * @ingroup forms
1914
 * @see system_settings_form()
1915
 * @see system_regional_settings_submit()
1916
 */
1917
function system_regional_settings() {
1918
  include_once DRUPAL_ROOT . '/includes/locale.inc';
1919
  $countries = country_get_list();
1920

    
1921
  // Date settings:
1922
  $zones = system_time_zones();
1923

    
1924
  $form['locale'] = array(
1925
    '#type' => 'fieldset',
1926
    '#title' => t('Locale'),
1927
  );
1928

    
1929
  $form['locale']['site_default_country'] = array(
1930
    '#type' => 'select',
1931
    '#title' => t('Default country'),
1932
    '#empty_value' => '',
1933
    '#default_value' => variable_get('site_default_country', ''),
1934
    '#options' => $countries,
1935
    '#attributes' => array('class' => array('country-detect')),
1936
  );
1937

    
1938
  $form['locale']['date_first_day'] = array(
1939
    '#type' => 'select',
1940
    '#title' => t('First day of week'),
1941
    '#default_value' => variable_get('date_first_day', 0),
1942
    '#options' => array(0 => t('Sunday'), 1 => t('Monday'), 2 => t('Tuesday'), 3 => t('Wednesday'), 4 => t('Thursday'), 5 => t('Friday'), 6 => t('Saturday')),
1943
  );
1944

    
1945
  $form['timezone'] = array(
1946
    '#type' => 'fieldset',
1947
    '#title' => t('Time zones'),
1948
  );
1949

    
1950
  $form['timezone']['date_default_timezone'] = array(
1951
    '#type' => 'select',
1952
    '#title' => t('Default time zone'),
1953
    '#default_value' => variable_get('date_default_timezone', date_default_timezone_get()),
1954
    '#options' => $zones,
1955
  );
1956

    
1957
  $configurable_timezones = variable_get('configurable_timezones', 1);
1958
  $form['timezone']['configurable_timezones'] = array(
1959
    '#type' => 'checkbox',
1960
    '#title' => t('Users may set their own time zone.'),
1961
    '#default_value' => $configurable_timezones,
1962
  );
1963

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

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

    
1993
  return system_settings_form($form);
1994
}
1995

    
1996
/**
1997
 * Form builder; Configure the site date and time settings.
1998
 *
1999
 * @ingroup forms
2000
 * @see system_settings_form()
2001
 */
2002
function system_date_time_settings() {
2003
  // Get list of all available date types.
2004
  drupal_static_reset('system_get_date_types');
2005
  $format_types = system_get_date_types();
2006

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

    
2033
      $choices = array();
2034
      foreach ($formats as $f => $format) {
2035
        $choices[$f] = format_date(REQUEST_TIME, 'custom', $f);
2036
      }
2037
      reset($formats);
2038
      $default = variable_get('date_format_' . $type, key($formats));
2039

    
2040
      // Get date type info for this date type.
2041
      $type_info = system_get_date_types($type);
2042
      $form['formats']['#theme'] = 'system_date_time_settings';
2043

    
2044
      // Show date format select list.
2045
      $form['formats']['format']['date_format_' . $type] = array(
2046
        '#type' => 'select',
2047
        '#title' => check_plain($type_info['title']),
2048
        '#attributes' => array('class' => array('date-format')),
2049
        '#default_value' => (isset($choices[$default]) ? $default : 'custom'),
2050
        '#options' => $choices,
2051
      );
2052

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

    
2065
  // Display a message if no date types configured.
2066
  $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')));
2067

    
2068
  return system_settings_form($form);
2069
}
2070

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

    
2088
  foreach (element_children($form['format']) as $key) {
2089
    $delete_key = $key . '_delete';
2090
    $row = array();
2091
    $row[] = $form['format'][$key]['#title'];
2092
    $form['format'][$key]['#title_display'] = 'invisible';
2093
    $row[] = array('data' => drupal_render($form['format'][$key]));
2094
    $row[] = array('data' => drupal_render($form['delete'][$delete_key]));
2095
    $rows[] = $row;
2096
  }
2097

    
2098
  $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'system-date-types')));
2099
  $output .= drupal_render_children($form);
2100

    
2101
  return $output;
2102
}
2103

    
2104

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

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

    
2150
  $form['actions'] = array('#type' => 'actions');
2151
  $form['actions']['submit'] = array(
2152
    '#type' => 'submit',
2153
    '#value' => t('Add date type'),
2154
  );
2155

    
2156
  $form['#validate'][] = 'system_add_date_format_type_form_validate';
2157
  $form['#submit'][] = 'system_add_date_format_type_form_submit';
2158

    
2159
  return $form;
2160
}
2161

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

    
2177
/**
2178
 * Process system_add_date_format_type form submissions.
2179
 */
2180
function system_add_date_format_type_form_submit($form, &$form_state) {
2181
  $machine_name = trim($form_state['values']['machine_name']);
2182

    
2183
  $format_type = array();
2184
  $format_type['title'] = trim($form_state['values']['date_type']);
2185
  $format_type['type'] = $machine_name;
2186
  $format_type['locked'] = 0;
2187
  $format_type['is_new'] = 1;
2188
  system_date_format_type_save($format_type);
2189
  variable_set('date_format_' . $machine_name, $form_state['values']['date_format']);
2190

    
2191
  drupal_set_message(t('New date type added successfully.'));
2192
  $form_state['redirect'] = 'admin/config/regional/date-time';
2193
}
2194

    
2195
/**
2196
 * Return the date for a given format string via Ajax.
2197
 */
2198
function system_date_time_lookup() {
2199
  $result = format_date(REQUEST_TIME, 'custom', $_GET['format']);
2200
  drupal_json_output($result);
2201
}
2202

    
2203
/**
2204
 * Form builder; Configure the site's maintenance status.
2205
 *
2206
 * @ingroup forms
2207
 * @see system_settings_form()
2208
 */
2209
function system_site_maintenance_mode() {
2210
  $form['maintenance_mode'] = array(
2211
    '#type' => 'checkbox',
2212
    '#title' => t('Put site into maintenance mode'),
2213
    '#default_value' => variable_get('maintenance_mode', 0),
2214
    '#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'))),
2215
  );
2216
  $form['maintenance_mode_message'] = array(
2217
    '#type' => 'textarea',
2218
    '#title' => t('Maintenance mode message'),
2219
    '#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')))),
2220
    '#description' => t('Message to show visitors when the site is in maintenance mode.')
2221
  );
2222

    
2223
  return system_settings_form($form);
2224
}
2225

    
2226
/**
2227
 * Form builder; Configure clean URL settings.
2228
 *
2229
 * @ingroup forms
2230
 * @see system_settings_form()
2231
 */
2232
function system_clean_url_settings($form, &$form_state) {
2233
  $available = FALSE;
2234
  $conflict = FALSE;
2235

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

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

    
2289
    $form_state['redirect'] = url('admin/config/search/clean-urls');
2290
    $form['clean_url_description'] = array(
2291
      '#type' => 'markup',
2292
      '#markup' => '<p>' . t('Use URLs like <code>example.com/user</code> instead of <code>example.com/?q=user</code>.'),
2293
    );
2294
    // Explain why the user is seeing this page and what to expect after
2295
    // clicking the 'Run the clean URL test' button.
2296
    $form['clean_url_test_result'] = array(
2297
      '#type' => 'markup',
2298
      '#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>',
2299
    );
2300
    $form['actions'] = array(
2301
      '#type' => 'actions',
2302
      'clean_url_test' => array(
2303
        '#type' => 'submit',
2304
        '#value' => t('Run the clean URL test'),
2305
      ),
2306
    );
2307
    $form['clean_url_test_execute'] = array(
2308
      '#type' => 'hidden',
2309
      '#value' => 1,
2310
    );
2311
  }
2312

    
2313
  return $form;
2314
}
2315

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

    
2327
  // Check run-time requirements and status information.
2328
  $requirements = module_invoke_all('requirements', 'runtime');
2329
  usort($requirements, '_system_sort_requirements');
2330

    
2331
  if ($check) {
2332
    return drupal_requirements_severity($requirements) == REQUIREMENT_ERROR;
2333
  }
2334
  // MySQL import might have set the uid of the anonymous user to autoincrement
2335
  // value. Let's try fixing it. See http://drupal.org/node/204411
2336
  db_update('users')
2337
    ->expression('uid', 'uid - uid')
2338
    ->condition('name', '')
2339
    ->condition('pass', '')
2340
    ->condition('status', 0)
2341
    ->execute();
2342
  return theme('status_report', array('requirements' => $requirements));
2343
}
2344

    
2345
/**
2346
 * Menu callback: run cron manually.
2347
 */
2348
function system_run_cron() {
2349
  // Run cron manually
2350
  if (drupal_cron_run()) {
2351
    drupal_set_message(t('Cron ran successfully.'));
2352
  }
2353
  else {
2354
    drupal_set_message(t('Cron run failed.'), 'error');
2355
  }
2356

    
2357
  drupal_goto('admin/reports/status');
2358
}
2359

    
2360
/**
2361
 * Menu callback: return information about PHP.
2362
 */
2363
function system_php() {
2364
  phpinfo();
2365
  drupal_exit();
2366
}
2367

    
2368
/**
2369
 * Default page callback for batches.
2370
 */
2371
function system_batch_page() {
2372
  require_once DRUPAL_ROOT . '/includes/batch.inc';
2373
  $output = _batch_page();
2374

    
2375
  if ($output === FALSE) {
2376
    drupal_access_denied();
2377
  }
2378
  elseif (isset($output)) {
2379
    // Force a page without blocks or messages to
2380
    // display a list of collected messages later.
2381
    drupal_set_page_content($output);
2382
    $page = element_info('page');
2383
    $page['#show_messages'] = FALSE;
2384
    return $page;
2385
  }
2386
}
2387

    
2388
/**
2389
 * Returns HTML for an administrative block for display.
2390
 *
2391
 * @param $variables
2392
 *   An associative array containing:
2393
 *   - block: An array containing information about the block:
2394
 *     - show: A Boolean whether to output the block. Defaults to FALSE.
2395
 *     - title: The block's title.
2396
 *     - content: (optional) Formatted content for the block.
2397
 *     - description: (optional) Description of the block. Only output if
2398
 *       'content' is not set.
2399
 *
2400
 * @ingroup themeable
2401
 */
2402
function theme_admin_block($variables) {
2403
  $block = $variables['block'];
2404
  $output = '';
2405

    
2406
  // Don't display the block if it has no content to display.
2407
  if (empty($block['show'])) {
2408
    return $output;
2409
  }
2410

    
2411
  $output .= '<div class="admin-panel">';
2412
  if (!empty($block['title'])) {
2413
    $output .= '<h3>' . $block['title'] . '</h3>';
2414
  }
2415
  if (!empty($block['content'])) {
2416
    $output .= '<div class="body">' . $block['content'] . '</div>';
2417
  }
2418
  else {
2419
    $output .= '<div class="description">' . $block['description'] . '</div>';
2420
  }
2421
  $output .= '</div>';
2422

    
2423
  return $output;
2424
}
2425

    
2426
/**
2427
 * Returns HTML for the content of an administrative block.
2428
 *
2429
 * @param $variables
2430
 *   An associative array containing:
2431
 *   - content: An array containing information about the block. Each element
2432
 *     of the array represents an administrative menu item, and must at least
2433
 *     contain the keys 'title', 'href', and 'localized_options', which are
2434
 *     passed to l(). A 'description' key may also be provided.
2435
 *
2436
 * @ingroup themeable
2437
 */
2438
function theme_admin_block_content($variables) {
2439
  $content = $variables['content'];
2440
  $output = '';
2441

    
2442
  if (!empty($content)) {
2443
    $class = 'admin-list';
2444
    if ($compact = system_admin_compact_mode()) {
2445
      $class .= ' compact';
2446
    }
2447
    $output .= '<dl class="' . $class . '">';
2448
    foreach ($content as $item) {
2449
      $output .= '<dt>' . l($item['title'], $item['href'], $item['localized_options']) . '</dt>';
2450
      if (!$compact && isset($item['description'])) {
2451
        $output .= '<dd>' . filter_xss_admin($item['description']) . '</dd>';
2452
      }
2453
    }
2454
    $output .= '</dl>';
2455
  }
2456
  return $output;
2457
}
2458

    
2459
/**
2460
 * Returns HTML for an administrative page.
2461
 *
2462
 * @param $variables
2463
 *   An associative array containing:
2464
 *   - blocks: An array of blocks to display. Each array should include a
2465
 *     'title', a 'description', a formatted 'content' and a 'position' which
2466
 *     will control which container it will be in. This is usually 'left' or
2467
 *     'right'.
2468
 *
2469
 * @ingroup themeable
2470
 */
2471
function theme_admin_page($variables) {
2472
  $blocks = $variables['blocks'];
2473

    
2474
  $stripe = 0;
2475
  $container = array();
2476

    
2477
  foreach ($blocks as $block) {
2478
    if ($block_output = theme('admin_block', array('block' => $block))) {
2479
      if (empty($block['position'])) {
2480
        // perform automatic striping.
2481
        $block['position'] = ++$stripe % 2 ? 'left' : 'right';
2482
      }
2483
      if (!isset($container[$block['position']])) {
2484
        $container[$block['position']] = '';
2485
      }
2486
      $container[$block['position']] .= $block_output;
2487
    }
2488
  }
2489

    
2490
  $output = '<div class="admin clearfix">';
2491
  $output .= theme('system_compact_link');
2492

    
2493
  foreach ($container as $id => $data) {
2494
    $output .= '<div class="' . $id . ' clearfix">';
2495
    $output .= $data;
2496
    $output .= '</div>';
2497
  }
2498
  $output .= '</div>';
2499
  return $output;
2500
}
2501

    
2502
/**
2503
 * Returns HTML for the output of the dashboard page.
2504
 *
2505
 * @param $variables
2506
 *   An associative array containing:
2507
 *   - menu_items: An array of modules to be displayed.
2508
 *
2509
 * @ingroup themeable
2510
 */
2511
function theme_system_admin_index($variables) {
2512
  $menu_items = $variables['menu_items'];
2513

    
2514
  $stripe = 0;
2515
  $container = array('left' => '', 'right' => '');
2516
  $flip = array('left' => 'right', 'right' => 'left');
2517
  $position = 'left';
2518

    
2519
  // Iterate over all modules.
2520
  foreach ($menu_items as $module => $block) {
2521
    list($description, $items) = $block;
2522

    
2523
    // Output links.
2524
    if (count($items)) {
2525
      $block = array();
2526
      $block['title'] = $module;
2527
      $block['content'] = theme('admin_block_content', array('content' => $items));
2528
      $block['description'] = t($description);
2529
      $block['show'] = TRUE;
2530

    
2531
      if ($block_output = theme('admin_block', array('block' => $block))) {
2532
        if (!isset($block['position'])) {
2533
          // Perform automatic striping.
2534
          $block['position'] = $position;
2535
          $position = $flip[$position];
2536
        }
2537
        $container[$block['position']] .= $block_output;
2538
      }
2539
    }
2540
  }
2541

    
2542
  $output = '<div class="admin clearfix">';
2543
  $output .= theme('system_compact_link');
2544
  foreach ($container as $id => $data) {
2545
    $output .= '<div class="' . $id . ' clearfix">';
2546
    $output .= $data;
2547
    $output .= '</div>';
2548
  }
2549
  $output .= '</div>';
2550

    
2551
  return $output;
2552
}
2553

    
2554
/**
2555
 * Returns HTML for the status report.
2556
 *
2557
 * @param $variables
2558
 *   An associative array containing:
2559
 *   - requirements: An array of requirements.
2560
 *
2561
 * @ingroup themeable
2562
 */
2563
function theme_status_report($variables) {
2564
  $requirements = $variables['requirements'];
2565
  $severities = array(
2566
    REQUIREMENT_INFO => array(
2567
      'title' => t('Info'),
2568
      'class' => 'info',
2569
    ),
2570
    REQUIREMENT_OK => array(
2571
      'title' => t('OK'),
2572
      'class' => 'ok',
2573
    ),
2574
    REQUIREMENT_WARNING => array(
2575
      'title' => t('Warning'),
2576
      'class' => 'warning',
2577
    ),
2578
    REQUIREMENT_ERROR => array(
2579
      'title' => t('Error'),
2580
      'class' => 'error',
2581
    ),
2582
  );
2583
  $output = '<table class="system-status-report">';
2584

    
2585
  foreach ($requirements as $requirement) {
2586
    if (empty($requirement['#type'])) {
2587
      $severity = $severities[isset($requirement['severity']) ? (int) $requirement['severity'] : REQUIREMENT_OK];
2588
      $severity['icon'] = '<div title="' . $severity['title'] . '"><span class="element-invisible">' . $severity['title'] . '</span></div>';
2589

    
2590
      // Output table row(s)
2591
      if (!empty($requirement['description'])) {
2592
        $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>';
2593
        $output .= '<tr class="' . $severity['class'] . ' merge-up"><td colspan="3" class="status-description">' . $requirement['description'] . '</td></tr>';
2594
      }
2595
      else {
2596
        $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>';
2597
      }
2598
    }
2599
  }
2600

    
2601
  $output .= '</table>';
2602
  return $output;
2603
}
2604

    
2605
/**
2606
 * Returns HTML for the modules form.
2607
 *
2608
 * @param $variables
2609
 *   An associative array containing:
2610
 *   - form: A render element representing the form.
2611
 *
2612
 * @ingroup themeable
2613
 */
2614
function theme_system_modules_fieldset($variables) {
2615
  $form = $variables['form'];
2616

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

    
2649
  return theme('table', array('header' => $form['#header'], 'rows' => $rows));
2650
}
2651

    
2652
/**
2653
 * Returns HTML for a message about incompatible modules.
2654
 *
2655
 * @param $variables
2656
 *   An associative array containing:
2657
 *   - message: The form array representing the currently disabled modules.
2658
 *
2659
 * @ingroup themeable
2660
 */
2661
function theme_system_modules_incompatible($variables) {
2662
  return '<div class="incompatible">' . $variables['message'] . '</div>';
2663
}
2664

    
2665
/**
2666
 * Returns HTML for a table of currently disabled modules.
2667
 *
2668
 * @param $variables
2669
 *   An associative array containing:
2670
 *   - form: A render element representing the form.
2671
 *
2672
 * @ingroup themeable
2673
 */
2674
function theme_system_modules_uninstall($variables) {
2675
  $form = $variables['form'];
2676

    
2677
  // No theming for the confirm form.
2678
  if (isset($form['confirm'])) {
2679
    return drupal_render($form);
2680
  }
2681

    
2682
  // Table headers.
2683
  $header = array(t('Uninstall'),
2684
    t('Name'),
2685
    t('Description'),
2686
  );
2687

    
2688
  // Display table.
2689
  $rows = array();
2690
  foreach (element_children($form['modules']) as $module) {
2691
    if (!empty($form['modules'][$module]['#required_by'])) {
2692
      $disabled_message = format_plural(count($form['modules'][$module]['#required_by']),
2693
        'To uninstall @module, the following module must be uninstalled first: @required_modules',
2694
        'To uninstall @module, the following modules must be uninstalled first: @required_modules',
2695
        array('@module' => $form['modules'][$module]['#module_name'], '@required_modules' => implode(', ', $form['modules'][$module]['#required_by'])));
2696
      $disabled_message = '<div class="admin-requirements">' . $disabled_message . '</div>';
2697
    }
2698
    else {
2699
      $disabled_message = '';
2700
    }
2701
    $rows[] = array(
2702
      array('data' => drupal_render($form['uninstall'][$module]), 'align' => 'center'),
2703
      '<strong><label for="' . $form['uninstall'][$module]['#id'] . '">' . drupal_render($form['modules'][$module]['name']) . '</label></strong>',
2704
      array('data' => drupal_render($form['modules'][$module]['description']) . $disabled_message, 'class' => array('description')),
2705
    );
2706
  }
2707

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

    
2711
  return $output;
2712
}
2713

    
2714
/**
2715
 * Returns HTML for the Appearance page.
2716
 *
2717
 * @param $variables
2718
 *   An associative array containing:
2719
 *   - theme_groups: An associative array containing groups of themes.
2720
 *
2721
 * @ingroup themeable
2722
 */
2723
function theme_system_themes_page($variables) {
2724
  $theme_groups = $variables['theme_groups'];
2725

    
2726
  $output = '<div id="system-themes-page">';
2727

    
2728
  foreach ($variables['theme_group_titles'] as $state => $title) {
2729
    if (!count($theme_groups[$state])) {
2730
      // Skip this group of themes if no theme is there.
2731
      continue;
2732
    }
2733
    // Start new theme group.
2734
    $output .= '<div class="system-themes-list system-themes-list-'. $state .' clearfix"><h2>'. $title .'</h2>';
2735

    
2736
    foreach ($theme_groups[$state] as $theme) {
2737

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

    
2741
      // Localize the theme description.
2742
      $description = t($theme->info['description']);
2743

    
2744
      // Style theme info
2745
      $notes = count($theme->notes) ? ' (' . join(', ', $theme->notes) . ')' : '';
2746
      $theme->classes[] = 'theme-selector';
2747
      $theme->classes[] = 'clearfix';
2748
      $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>';
2749

    
2750
      // Make sure to provide feedback on compatibility.
2751
      if (!empty($theme->incompatible_core)) {
2752
        $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>';
2753
      }
2754
      elseif (!empty($theme->incompatible_php)) {
2755
        if (substr_count($theme->info['php'], '.') < 2) {
2756
          $theme->info['php'] .= '.*';
2757
        }
2758
        $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>';
2759
      }
2760
      else {
2761
        $output .= theme('links', array('links' => $theme->operations, 'attributes' => array('class' => array('operations', 'clearfix'))));
2762
      }
2763
      $output .= '</div></div>';
2764
    }
2765
    $output .= '</div>';
2766
  }
2767
  $output .= '</div>';
2768

    
2769
  return $output;
2770
}
2771

    
2772
/**
2773
 * Menu callback; present a form for deleting a date format.
2774
 */
2775
function system_date_delete_format_form($form, &$form_state, $dfid) {
2776
  $form['dfid'] = array(
2777
    '#type' => 'value',
2778
    '#value' => $dfid,
2779
  );
2780
  $format = system_get_date_format($dfid);
2781

    
2782
  $output = confirm_form($form,
2783
    t('Are you sure you want to remove the format %format?', array('%format' => format_date(REQUEST_TIME, 'custom', $format->format))),
2784
    'admin/config/regional/date-time/formats',
2785
    t('This action cannot be undone.'),
2786
    t('Remove'), t('Cancel'),
2787
    'confirm'
2788
  );
2789

    
2790
  return $output;
2791
}
2792

    
2793
/**
2794
 * Delete a configured date format.
2795
 */
2796
function system_date_delete_format_form_submit($form, &$form_state) {
2797
  if ($form_state['values']['confirm']) {
2798
    $format = system_get_date_format($form_state['values']['dfid']);
2799
    system_date_format_delete($form_state['values']['dfid']);
2800
    drupal_set_message(t('Removed date format %format.', array('%format' => format_date(REQUEST_TIME, 'custom', $format->format))));
2801
    $form_state['redirect'] = 'admin/config/regional/date-time/formats';
2802
  }
2803
}
2804

    
2805
/**
2806
 * Menu callback; present a form for deleting a date type.
2807
 */
2808
function system_delete_date_format_type_form($form, &$form_state, $format_type) {
2809
  $form['format_type'] = array(
2810
    '#type' => 'value',
2811
    '#value' => $format_type,
2812
  );
2813
  $type_info = system_get_date_types($format_type);
2814

    
2815
  $output = confirm_form($form,
2816
    t('Are you sure you want to remove the date type %type?', array('%type' => $type_info['title'])),
2817
    'admin/config/regional/date-time',
2818
    t('This action cannot be undone.'),
2819
    t('Remove'), t('Cancel'),
2820
    'confirm'
2821
  );
2822

    
2823
  return $output;
2824
}
2825

    
2826
/**
2827
 * Delete a configured date type.
2828
 */
2829
function system_delete_date_format_type_form_submit($form, &$form_state) {
2830
  if ($form_state['values']['confirm']) {
2831
    $type_info = system_get_date_types($form_state['values']['format_type']);
2832
    system_date_format_type_delete($form_state['values']['format_type']);
2833
    drupal_set_message(t('Removed date type %type.', array('%type' => $type_info['title'])));
2834
    $form_state['redirect'] = 'admin/config/regional/date-time';
2835
  }
2836
}
2837

    
2838

    
2839
/**
2840
 * Displays the date format strings overview page.
2841
 */
2842
function system_date_time_formats() {
2843
  $header = array(t('Format'), array('data' => t('Operations'), 'colspan' => '2'));
2844
  $rows = array();
2845

    
2846
  drupal_static_reset('system_get_date_formats');
2847
  $formats = system_get_date_formats('custom');
2848
  if (!empty($formats)) {
2849
    foreach ($formats as $format) {
2850
      $row = array();
2851
      $row[] = array('data' => format_date(REQUEST_TIME, 'custom', $format['format']));
2852
      $row[] = array('data' => l(t('edit'), 'admin/config/regional/date-time/formats/' . $format['dfid'] . '/edit'));
2853
      $row[] = array('data' => l(t('delete'), 'admin/config/regional/date-time/formats/' . $format['dfid'] . '/delete'));
2854
      $rows[] = $row;
2855
    }
2856
  }
2857

    
2858
  $build['date_formats_table'] = array(
2859
    '#theme' => 'table',
2860
    '#header' => $header,
2861
    '#rows' => $rows,
2862
    '#empty' => t('No custom date formats available. <a href="@link">Add date format</a>.', array('@link' => url('admin/config/regional/date-time/formats/add'))),
2863
  );
2864

    
2865
  return $build;
2866
}
2867

    
2868
/**
2869
 * Allow users to add additional date formats.
2870
 */
2871
function system_configure_date_formats_form($form, &$form_state, $dfid = 0) {
2872
  $js_settings = array(
2873
    'type' => 'setting',
2874
    'data' => array(
2875
      'dateTime' => array(
2876
        'date-format' => array(
2877
          'text' => t('Displayed as'),
2878
          'lookup' => url('admin/config/regional/date-time/formats/lookup'),
2879
        ),
2880
      ),
2881
    ),
2882
  );
2883

    
2884
  if ($dfid) {
2885
    $form['dfid'] = array(
2886
      '#type' => 'value',
2887
      '#value' => $dfid,
2888
    );
2889
    $format = system_get_date_format($dfid);
2890
  }
2891

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

    
2894
  $form['date_format'] = array(
2895
    '#type' => 'textfield',
2896
    '#title' => t('Format string'),
2897
    '#maxlength' => 100,
2898
    '#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')),
2899
    '#default_value' => ($dfid ? $format->format : ''),
2900
    '#field_suffix' => ' <small id="edit-date-format-suffix">' . $now . '</small>',
2901
    '#attached' => array(
2902
      'js' => array(drupal_get_path('module', 'system') . '/system.js', $js_settings),
2903
    ),
2904
    '#required' => TRUE,
2905
  );
2906

    
2907
  $form['actions'] = array('#type' => 'actions');
2908
  $form['actions']['update'] = array(
2909
    '#type' => 'submit',
2910
    '#value' => ($dfid ? t('Save format') : t('Add format')),
2911
  );
2912

    
2913
  $form['#validate'][] = 'system_add_date_formats_form_validate';
2914
  $form['#submit'][] = 'system_add_date_formats_form_submit';
2915

    
2916
  return $form;
2917
}
2918

    
2919
/**
2920
 * Validate new date format string submission.
2921
 */
2922
function system_add_date_formats_form_validate($form, &$form_state) {
2923
  $formats = system_get_date_formats('custom');
2924
  $format = trim($form_state['values']['date_format']);
2925
  if (!empty($formats) && in_array($format, array_keys($formats)) && (!isset($form_state['values']['dfid']) || $form_state['values']['dfid'] != $formats[$format]['dfid'])) {
2926
    form_set_error('date_format', t('This format already exists. Enter a unique format string.'));
2927
  }
2928
}
2929

    
2930
/**
2931
 * Process new date format string submission.
2932
 */
2933
function system_add_date_formats_form_submit($form, &$form_state) {
2934
  $format = array();
2935
  $format['format'] = trim($form_state['values']['date_format']);
2936
  $format['type'] = 'custom';
2937
  $format['locked'] = 0;
2938
  if (!empty($form_state['values']['dfid'])) {
2939
    system_date_format_save($format, $form_state['values']['dfid']);
2940
    drupal_set_message(t('Custom date format updated.'));
2941
  }
2942
  else {
2943
    $format['is_new'] = 1;
2944
    system_date_format_save($format);
2945
    drupal_set_message(t('Custom date format added.'));
2946
  }
2947

    
2948
  $form_state['redirect'] = 'admin/config/regional/date-time/formats';
2949
}
2950

    
2951
/**
2952
 * Menu callback; Displays an overview of available and configured actions.
2953
 */
2954
function system_actions_manage() {
2955
  actions_synchronize();
2956
  $actions = actions_list();
2957
  $actions_map = actions_actions_map($actions);
2958
  $options = array();
2959
  $unconfigurable = array();
2960

    
2961
  foreach ($actions_map as $key => $array) {
2962
    if ($array['configurable']) {
2963
      $options[$key] = $array['label'] . '...';
2964
    }
2965
    else {
2966
      $unconfigurable[] = $array;
2967
    }
2968
  }
2969

    
2970
  $row = array();
2971
  $instances_present = db_query("SELECT aid FROM {actions} WHERE parameters <> ''")->fetchField();
2972
  $header = array(
2973
    array('data' => t('Action type'), 'field' => 'type'),
2974
    array('data' => t('Label'), 'field' => 'label'),
2975
    array('data' => $instances_present ? t('Operations') : '', 'colspan' => '2')
2976
  );
2977
  $query = db_select('actions')->extend('PagerDefault')->extend('TableSort');
2978
  $result = $query
2979
    ->fields('actions')
2980
    ->limit(50)
2981
    ->orderByHeader($header)
2982
    ->execute();
2983

    
2984
  foreach ($result as $action) {
2985
    $row[] = array(
2986
      array('data' => $action->type),
2987
      array('data' => check_plain($action->label)),
2988
      array('data' => $action->parameters ? l(t('configure'), "admin/config/system/actions/configure/$action->aid") : ''),
2989
      array('data' => $action->parameters ? l(t('delete'), "admin/config/system/actions/delete/$action->aid") : '')
2990
    );
2991
  }
2992

    
2993
  if ($row) {
2994
    $pager = theme('pager');
2995
    if (!empty($pager)) {
2996
      $row[] = array(array('data' => $pager, 'colspan' => '3'));
2997
    }
2998
    $build['system_actions_header'] = array('#markup' => '<h3>' . t('Available actions:') . '</h3>');
2999
    $build['system_actions_table'] = array('#markup' => theme('table', array('header' => $header, 'rows' => $row)));
3000
  }
3001

    
3002
  if ($actions_map) {
3003
    $build['system_actions_manage_form'] = drupal_get_form('system_actions_manage_form', $options);
3004
  }
3005

    
3006
  return $build;
3007
}
3008

    
3009
/**
3010
 * Define the form for the actions overview page.
3011
 *
3012
 * @param $form_state
3013
 *   An associative array containing the current state of the form; not used.
3014
 * @param $options
3015
 *   An array of configurable actions.
3016
 * @return
3017
 *   Form definition.
3018
 *
3019
 * @ingroup forms
3020
 * @see system_actions_manage_form_submit()
3021
 */
3022
function system_actions_manage_form($form, &$form_state, $options = array()) {
3023
  $form['parent'] = array(
3024
    '#type' => 'fieldset',
3025
    '#title' => t('Create an advanced action'),
3026
    '#attributes' => array('class' => array('container-inline')),
3027
  );
3028
  $form['parent']['action'] = array(
3029
    '#type' => 'select',
3030
    '#title' => t('Action'),
3031
    '#title_display' => 'invisible',
3032
    '#options' => $options,
3033
    '#empty_option' => t('Choose an advanced action'),
3034
  );
3035
  $form['parent']['actions'] = array('#type' => 'actions');
3036
  $form['parent']['actions']['submit'] = array(
3037
    '#type' => 'submit',
3038
    '#value' => t('Create'),
3039
  );
3040
  return $form;
3041
}
3042

    
3043
/**
3044
 * Process system_actions_manage form submissions.
3045
 *
3046
 * @see system_actions_manage_form()
3047
 */
3048
function system_actions_manage_form_submit($form, &$form_state) {
3049
  if ($form_state['values']['action']) {
3050
    $form_state['redirect'] = 'admin/config/system/actions/configure/' . $form_state['values']['action'];
3051
  }
3052
}
3053

    
3054
/**
3055
 * Menu callback; Creates the form for configuration of a single action.
3056
 *
3057
 * We provide the "Description" field. The rest of the form is provided by the
3058
 * action. We then provide the Save button. Because we are combining unknown
3059
 * form elements with the action configuration form, we use an 'actions_' prefix
3060
 * on our elements.
3061
 *
3062
 * @param $action
3063
 *   Hash of an action ID or an integer. If it is a hash, we are
3064
 *   creating a new instance. If it is an integer, we are editing an existing
3065
 *   instance.
3066
 * @return
3067
 *   A form definition.
3068
 *
3069
 * @see system_actions_configure_validate()
3070
 * @see system_actions_configure_submit()
3071
 */
3072
function system_actions_configure($form, &$form_state, $action = NULL) {
3073
  if ($action === NULL) {
3074
    drupal_goto('admin/config/system/actions');
3075
  }
3076

    
3077
  $actions_map = actions_actions_map(actions_list());
3078
  $edit = array();
3079

    
3080
  // Numeric action denotes saved instance of a configurable action.
3081
  if (is_numeric($action)) {
3082
    $aid = $action;
3083
    // Load stored parameter values from database.
3084
    $data = db_query("SELECT * FROM {actions} WHERE aid = :aid", array(':aid' => $aid))->fetch();
3085
    $edit['actions_label'] = $data->label;
3086
    $edit['actions_type'] = $data->type;
3087
    $function = $data->callback;
3088
    $action = drupal_hash_base64($data->callback);
3089
    $params = unserialize($data->parameters);
3090
    if ($params) {
3091
      foreach ($params as $name => $val) {
3092
        $edit[$name] = $val;
3093
      }
3094
    }
3095
  }
3096
  // Otherwise, we are creating a new action instance.
3097
  else {
3098
    $function = $actions_map[$action]['callback'];
3099
    $edit['actions_label'] = $actions_map[$action]['label'];
3100
    $edit['actions_type'] = $actions_map[$action]['type'];
3101
  }
3102

    
3103
  $form['actions_label'] = array(
3104
    '#type' => 'textfield',
3105
    '#title' => t('Label'),
3106
    '#default_value' => $edit['actions_label'],
3107
    '#maxlength' => '255',
3108
    '#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.'),
3109
    '#weight' => -10
3110
  );
3111
  $action_form = $function . '_form';
3112
  $form = array_merge($form, $action_form($edit));
3113
  $form['actions_type'] = array(
3114
    '#type' => 'value',
3115
    '#value' => $edit['actions_type'],
3116
  );
3117
  $form['actions_action'] = array(
3118
    '#type' => 'hidden',
3119
    '#value' => $action,
3120
  );
3121
  // $aid is set when configuring an existing action instance.
3122
  if (isset($aid)) {
3123
    $form['actions_aid'] = array(
3124
      '#type' => 'hidden',
3125
      '#value' => $aid,
3126
    );
3127
  }
3128
  $form['actions_configured'] = array(
3129
    '#type' => 'hidden',
3130
    '#value' => '1',
3131
  );
3132
  $form['actions'] = array('#type' => 'actions');
3133
  $form['actions']['submit'] = array(
3134
    '#type' => 'submit',
3135
    '#value' => t('Save'),
3136
    '#weight' => 13
3137
  );
3138

    
3139
  return $form;
3140
}
3141

    
3142
/**
3143
 * Validate system_actions_configure() form submissions.
3144
 */
3145
function system_actions_configure_validate($form, &$form_state) {
3146
  $function = actions_function_lookup($form_state['values']['actions_action']) . '_validate';
3147
  // Hand off validation to the action.
3148
  if (function_exists($function)) {
3149
    $function($form, $form_state);
3150
  }
3151
}
3152

    
3153
/**
3154
 * Process system_actions_configure() form submissions.
3155
 */
3156
function system_actions_configure_submit($form, &$form_state) {
3157
  $function = actions_function_lookup($form_state['values']['actions_action']);
3158
  $submit_function = $function . '_submit';
3159

    
3160
  // Action will return keyed array of values to store.
3161
  $params = $submit_function($form, $form_state);
3162
  $aid = isset($form_state['values']['actions_aid']) ? $form_state['values']['actions_aid'] : NULL;
3163

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

    
3167
  $form_state['redirect'] = 'admin/config/system/actions/manage';
3168
}
3169

    
3170
/**
3171
 * Create the form for confirmation of deleting an action.
3172
 *
3173
 * @see system_actions_delete_form_submit()
3174
 * @ingroup forms
3175
 */
3176
function system_actions_delete_form($form, &$form_state, $action) {
3177
  $form['aid'] = array(
3178
    '#type' => 'hidden',
3179
    '#value' => $action->aid,
3180
  );
3181
  return confirm_form($form,
3182
    t('Are you sure you want to delete the action %action?', array('%action' => $action->label)),
3183
    'admin/config/system/actions/manage',
3184
    t('This cannot be undone.'),
3185
    t('Delete'),
3186
    t('Cancel')
3187
  );
3188
}
3189

    
3190
/**
3191
 * Process system_actions_delete form submissions.
3192
 *
3193
 * Post-deletion operations for action deletion.
3194
 */
3195
function system_actions_delete_form_submit($form, &$form_state) {
3196
  $aid = $form_state['values']['aid'];
3197
  $action = actions_load($aid);
3198
  actions_delete($aid);
3199
  watchdog('user', 'Deleted action %aid (%action)', array('%aid' => $aid, '%action' => $action->label));
3200
  drupal_set_message(t('Action %action was deleted', array('%action' => $action->label)));
3201
  $form_state['redirect'] = 'admin/config/system/actions/manage';
3202
}
3203

    
3204
/**
3205
 * Post-deletion operations for deleting action orphans.
3206
 *
3207
 * @param $orphaned
3208
 *   An array of orphaned actions.
3209
 */
3210
function system_action_delete_orphans_post($orphaned) {
3211
  foreach ($orphaned as $callback) {
3212
    drupal_set_message(t("Deleted orphaned action (%action).", array('%action' => $callback)));
3213
  }
3214
}
3215

    
3216
/**
3217
 * Remove actions that are in the database but not supported by any enabled module.
3218
 */
3219
function system_actions_remove_orphans() {
3220
  actions_synchronize(TRUE);
3221
  drupal_goto('admin/config/system/actions/manage');
3222
}