Projet

Général

Profil

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

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

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

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

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

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

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

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

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

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

    
1052
  return $form;
1053
}
1054

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1236
  $form_state['redirect'] = 'admin/modules';
1237
}
1238

    
1239
/**
1240
 * Uninstall functions
1241
 */
1242

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

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

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

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

    
1310
  return $form;
1311
}
1312

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

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

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

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

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

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

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

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

    
1403
  $build['system_ip_blocking_form'] = drupal_get_form('system_ip_blocking_form', $default_ip);
1404

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

    
1412
  return $build;
1413
}
1414

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

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

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

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

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

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

    
1559
  $form['#validate'][] = 'system_site_information_settings_validate';
1560

    
1561
  return system_settings_form($form);
1562
}
1563

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

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

    
1623
  $form['cron_url'] = array(
1624
    '#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>',
1625
  );
1626

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

    
1638
  return system_settings_form($form);
1639
}
1640

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

    
1655
  drupal_goto('admin/config/system/cron');
1656
}
1657

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

    
1677
  return system_settings_form($form);
1678
}
1679

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

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

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

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

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

    
1729
  $directory = 'public://';
1730
  $is_writable = is_dir($directory) && is_writable($directory);
1731
  $disabled = !$is_writable;
1732
  $disabled_message = '';
1733
  if (!$is_writable) {
1734
    $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')));
1735
  }
1736

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

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

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

    
1770
  return system_settings_form($form);
1771
}
1772

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

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

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

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

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

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

    
1841
  return system_settings_form($form);
1842
}
1843

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

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

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

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

    
1880
  return system_settings_form($form);
1881
}
1882

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

    
1911
  return system_settings_form($form);
1912
}
1913

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

    
1925
  // Date settings:
1926
  $zones = system_time_zones();
1927

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

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

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

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

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

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

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

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

    
1997
  return system_settings_form($form);
1998
}
1999

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

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

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

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

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

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

    
2069
  // Display a message if no date types configured.
2070
  $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')));
2071

    
2072
  return system_settings_form($form);
2073
}
2074

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

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

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

    
2105
  return $output;
2106
}
2107

    
2108

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

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

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

    
2160
  $form['#validate'][] = 'system_add_date_format_type_form_validate';
2161
  $form['#submit'][] = 'system_add_date_format_type_form_submit';
2162

    
2163
  return $form;
2164
}
2165

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

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

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

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

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

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

    
2227
  return system_settings_form($form);
2228
}
2229

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

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

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

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

    
2317
  return $form;
2318
}
2319

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

    
2331
  // Check run-time requirements and status information.
2332
  $requirements = module_invoke_all('requirements', 'runtime');
2333
  usort($requirements, '_system_sort_requirements');
2334

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

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

    
2361
  drupal_goto('admin/reports/status');
2362
}
2363

    
2364
/**
2365
 * Menu callback: return information about PHP.
2366
 */
2367
function system_php() {
2368
  phpinfo();
2369
  drupal_exit();
2370
}
2371

    
2372
/**
2373
 * Default page callback for batches.
2374
 */
2375
function system_batch_page() {
2376
  require_once DRUPAL_ROOT . '/includes/batch.inc';
2377
  $output = _batch_page();
2378

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

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

    
2410
  // Don't display the block if it has no content to display.
2411
  if (empty($block['show'])) {
2412
    return $output;
2413
  }
2414

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

    
2427
  return $output;
2428
}
2429

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

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

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

    
2478
  $stripe = 0;
2479
  $container = array();
2480

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

    
2494
  $output = '<div class="admin clearfix">';
2495
  $output .= theme('system_compact_link');
2496

    
2497
  foreach ($container as $id => $data) {
2498
    $output .= '<div class="' . $id . ' clearfix">';
2499
    $output .= $data;
2500
    $output .= '</div>';
2501
  }
2502
  $output .= '</div>';
2503
  return $output;
2504
}
2505

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

    
2518
  $stripe = 0;
2519
  $container = array('left' => '', 'right' => '');
2520
  $flip = array('left' => 'right', 'right' => 'left');
2521
  $position = 'left';
2522

    
2523
  // Iterate over all modules.
2524
  foreach ($menu_items as $module => $block) {
2525
    list($description, $items) = $block;
2526

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

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

    
2546
  $output = '<div class="admin clearfix">';
2547
  $output .= theme('system_compact_link');
2548
  foreach ($container as $id => $data) {
2549
    $output .= '<div class="' . $id . ' clearfix">';
2550
    $output .= $data;
2551
    $output .= '</div>';
2552
  }
2553
  $output .= '</div>';
2554

    
2555
  return $output;
2556
}
2557

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

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

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

    
2605
  $output .= '</table>';
2606
  return $output;
2607
}
2608

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

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

    
2653
  return theme('table', array('header' => $form['#header'], 'rows' => $rows));
2654
}
2655

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

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

    
2681
  // No theming for the confirm form.
2682
  if (isset($form['confirm'])) {
2683
    return drupal_render($form);
2684
  }
2685

    
2686
  // Table headers.
2687
  $header = array(t('Uninstall'),
2688
    t('Name'),
2689
    t('Description'),
2690
  );
2691

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

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

    
2715
  return $output;
2716
}
2717

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

    
2730
  $output = '<div id="system-themes-page">';
2731

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

    
2740
    foreach ($theme_groups[$state] as $theme) {
2741

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

    
2745
      // Localize the theme description.
2746
      $description = t($theme->info['description']);
2747

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

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

    
2773
  return $output;
2774
}
2775

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

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

    
2794
  return $output;
2795
}
2796

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

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

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

    
2827
  return $output;
2828
}
2829

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

    
2842

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

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

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

    
2869
  return $build;
2870
}
2871

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

    
2888
  if ($dfid) {
2889
    $form['dfid'] = array(
2890
      '#type' => 'value',
2891
      '#value' => $dfid,
2892
    );
2893
    $format = system_get_date_format($dfid);
2894
  }
2895

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

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

    
2911
  $form['actions'] = array('#type' => 'actions');
2912
  $form['actions']['update'] = array(
2913
    '#type' => 'submit',
2914
    '#value' => ($dfid ? t('Save format') : t('Add format')),
2915
  );
2916

    
2917
  $form['#validate'][] = 'system_add_date_formats_form_validate';
2918
  $form['#submit'][] = 'system_add_date_formats_form_submit';
2919

    
2920
  return $form;
2921
}
2922

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

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

    
2952
  $form_state['redirect'] = 'admin/config/regional/date-time/formats';
2953
}
2954

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

    
2965
  foreach ($actions_map as $key => $array) {
2966
    if ($array['configurable']) {
2967
      $options[$key] = $array['label'] . '...';
2968
    }
2969
    else {
2970
      $unconfigurable[] = $array;
2971
    }
2972
  }
2973

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

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

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

    
3006
  if ($actions_map) {
3007
    $build['system_actions_manage_form'] = drupal_get_form('system_actions_manage_form', $options);
3008
  }
3009

    
3010
  return $build;
3011
}
3012

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

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

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

    
3081
  $actions_map = actions_actions_map(actions_list());
3082
  $edit = array();
3083

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

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

    
3143
  return $form;
3144
}
3145

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

    
3157
/**
3158
 * Process system_actions_configure() form submissions.
3159
 */
3160
function system_actions_configure_submit($form, &$form_state) {
3161
  $function = actions_function_lookup($form_state['values']['actions_action']);
3162
  $submit_function = $function . '_submit';
3163

    
3164
  // Action will return keyed array of values to store.
3165
  $params = $submit_function($form, $form_state);
3166
  $aid = isset($form_state['values']['actions_aid']) ? $form_state['values']['actions_aid'] : NULL;
3167

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

    
3171
  $form_state['redirect'] = 'admin/config/system/actions/manage';
3172
}
3173

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

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

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

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