Projet

Général

Profil

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

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

1
<?php
2

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

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

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

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

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

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

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

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

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

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

    
130
  return $output;
131
}
132

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
368
      // The status message depends on whether an admin theme is currently in use:
369
      // a value of 0 means the admin theme is set to be the default theme.
370
      $admin_theme = variable_get('admin_theme', 0);
371
      if ($admin_theme != 0 && $admin_theme != $theme) {
372
        drupal_set_message(t('Please note that the administration theme is still set to the %admin_theme theme; consequently, the theme on this page remains unchanged. All non-administrative sections of the site, however, will show the selected %selected_theme theme by default.', array(
373
          '%admin_theme' => $themes[$admin_theme]->info['name'],
374
          '%selected_theme' => $themes[$theme]->info['name'],
375
        )));
376
      }
377
      else {
378
        drupal_set_message(t('%theme is now the default theme.', array('%theme' => $themes[$theme]->info['name'])));
379
      }
380
    }
381
    else {
382
      drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
383
    }
384
    drupal_goto('admin/appearance');
385
  }
386
  return MENU_ACCESS_DENIED;
387
}
388

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
739
  cache_clear_all();
740
}
741

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
944
  return $form;
945
}
946

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

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

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

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

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

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

    
1017
  // Ensure this module is compatible with the currently installed version of PHP.
1018
  if (version_compare(phpversion(), $info['php']) < 0) {
1019
    $compatible = FALSE;
1020
    $reasons_short[] = t('Incompatible with this version of PHP');
1021
    $php_required = $info['php'];
1022
    if (substr_count($info['php'], '.') < 2) {
1023
      $php_required .= '.*';
1024
    }
1025
    $reasons_long[] = t('This module requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $php_required, '!php_version' => phpversion()));
1026
  }
1027

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

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

    
1054
  return $form;
1055
}
1056

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1312
  return $form;
1313
}
1314

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

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

    
1339
  // Display a confirm form if modules have been selected.
1340
  if (isset($uninstall)) {
1341
    $form['#confirmed'] = TRUE;
1342
    $form['uninstall']['#tree'] = TRUE;
1343
    $form['modules'] = array('#markup' => '<p>' . t('The following modules will be completely uninstalled from your site, and <em>all data from these modules will be lost</em>!') . '</p>' . theme('item_list', array('items' => $uninstall)));
1344
    $form = confirm_form(
1345
      $form,
1346
      t('Confirm uninstall'),
1347
      'admin/modules/uninstall',
1348
      t('Would you like to continue with uninstalling the above?'),
1349
      t('Uninstall'),
1350
      t('Cancel'));
1351
    return $form;
1352
  }
1353
}
1354

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

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

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

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

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

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

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

    
1414
  return $build;
1415
}
1416

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

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

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

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

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

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

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

    
1563
  return system_settings_form($form);
1564
}
1565

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

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

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

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

    
1640
  return system_settings_form($form);
1641
}
1642

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

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

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

    
1679
  return system_settings_form($form);
1680
}
1681

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

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

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

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

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

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

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

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

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

    
1772
  return system_settings_form($form);
1773
}
1774

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

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

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

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

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

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

    
1843
  return system_settings_form($form);
1844
}
1845

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

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

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

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

    
1882
  return system_settings_form($form);
1883
}
1884

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

    
1913
  return system_settings_form($form);
1914
}
1915

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

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

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

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

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

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

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

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

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

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

    
1999
  return system_settings_form($form);
2000
}
2001

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

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

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

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

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

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

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

    
2074
  return system_settings_form($form);
2075
}
2076

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

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

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

    
2107
  return $output;
2108
}
2109

    
2110

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

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

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

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

    
2165
  return $form;
2166
}
2167

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

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

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

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

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

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

    
2229
  return system_settings_form($form);
2230
}
2231

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

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

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

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

    
2319
  return $form;
2320
}
2321

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

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

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

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

    
2363
  drupal_goto('admin/reports/status');
2364
}
2365

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

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

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

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

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

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

    
2429
  return $output;
2430
}
2431

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

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

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

    
2480
  $stripe = 0;
2481
  $container = array();
2482

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

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

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

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

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

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

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

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

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

    
2557
  return $output;
2558
}
2559

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

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

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

    
2607
  $output .= '</table>';
2608
  return $output;
2609
}
2610

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

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

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

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

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

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

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

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

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

    
2717
  return $output;
2718
}
2719

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

    
2732
  $output = '<div id="system-themes-page">';
2733

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

    
2742
    foreach ($theme_groups[$state] as $theme) {
2743

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

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

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

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

    
2775
  return $output;
2776
}
2777

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

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

    
2796
  return $output;
2797
}
2798

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

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

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

    
2829
  return $output;
2830
}
2831

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

    
2844

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

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

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

    
2871
  return $build;
2872
}
2873

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

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

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

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

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

    
2919
  $form['#validate'][] = 'system_add_date_formats_form_validate';
2920
  $form['#submit'][] = 'system_add_date_formats_form_submit';
2921

    
2922
  return $form;
2923
}
2924

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

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

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

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

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

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

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

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

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

    
3012
  return $build;
3013
}
3014

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

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

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

    
3083
  $actions_map = actions_actions_map(actions_list());
3084
  $edit = array();
3085

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

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

    
3145
  return $form;
3146
}
3147

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

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

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

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

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

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

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

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

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