Projet

Général

Profil

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

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

1 85ad3d82 Assos Assos
<?php
2
3
/**
4
 * @file
5
 * Admin page callbacks for the system module.
6
 */
7
8
/**
9
 * Menu callback; Provide the administration overview page.
10
 */
11
function system_admin_config_page() {
12
  // Check for status report errors.
13
  if (system_status(TRUE) && user_access('administer site configuration')) {
14
    drupal_set_message(t('One or more problems were detected with your Drupal installation. Check the <a href="@status">status report</a> for more information.', array('@status' => url('admin/reports/status'))), 'error');
15
  }
16
  $blocks = array();
17
  if ($admin = db_query("SELECT menu_name, mlid FROM {menu_links} WHERE link_path = 'admin/config' AND module = 'system'")->fetchAssoc()) {
18
    $result = db_query("
19
      SELECT m.*, ml.*
20
      FROM {menu_links} ml
21
      INNER JOIN {menu_router} m ON ml.router_path = m.path
22
      WHERE ml.link_path <> 'admin/help' AND menu_name = :menu_name AND ml.plid = :mlid AND hidden = 0", $admin, array('fetch' => PDO::FETCH_ASSOC));
23
    foreach ($result as $item) {
24
      _menu_link_translate($item);
25
      if (!$item['access']) {
26
        continue;
27
      }
28
      // The link description, either derived from 'description' in hook_menu()
29
      // or customized via menu module is used as title attribute.
30
      if (!empty($item['localized_options']['attributes']['title'])) {
31
        $item['description'] = $item['localized_options']['attributes']['title'];
32
        unset($item['localized_options']['attributes']['title']);
33
      }
34
      $block = $item;
35
      $block['content'] = '';
36
      $block['content'] .= theme('admin_block_content', array('content' => system_admin_menu_block($item)));
37
      if (!empty($block['content'])) {
38
        $block['show'] = TRUE;
39
      }
40
41
      // Prepare for sorting as in function _menu_tree_check_access().
42
      // The weight is offset so it is always positive, with a uniform 5-digits.
43
      $blocks[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' . $item['mlid']] = $block;
44
    }
45
  }
46
  if ($blocks) {
47
    ksort($blocks);
48
    return theme('admin_page', array('blocks' => $blocks));
49
  }
50
  else {
51
    return t('You do not have any administrative items.');
52
  }
53
}
54
55
/**
56
 * Provide a single block from the administration menu as a page.
57
 *
58
 * This function is often a destination for these blocks.
59
 * For example, 'admin/structure/types' needs to have a destination to be valid
60
 * in the Drupal menu system, but too much information there might be
61
 * hidden, so we supply the contents of the block.
62
 *
63
 * @return
64
 *   The output HTML.
65
 */
66
function system_admin_menu_block_page() {
67
  $item = menu_get_item();
68
  if ($content = system_admin_menu_block($item)) {
69
    $output = theme('admin_block_content', array('content' => $content));
70
  }
71
  else {
72
    $output = t('You do not have any administrative items.');
73
  }
74
  return $output;
75
}
76
77
/**
78
 * Menu callback; prints a listing of admin tasks, organized by module.
79
 */
80
function system_admin_index() {
81
  $module_info = system_get_info('module');
82
  foreach ($module_info as $module => $info) {
83
    $module_info[$module] = new stdClass();
84
    $module_info[$module]->info = $info;
85
  }
86
  uasort($module_info, 'system_sort_modules_by_info_name');
87
  $menu_items = array();
88
89
  foreach ($module_info as $module => $info) {
90
    // Only display a section if there are any available tasks.
91
    if ($admin_tasks = system_get_module_admin_tasks($module, $info->info)) {
92
      // Sort links by title.
93
      uasort($admin_tasks, 'drupal_sort_title');
94
      // Move 'Configure permissions' links to the bottom of each section.
95
      $permission_key = "admin/people/permissions#module-$module";
96
      if (isset($admin_tasks[$permission_key])) {
97
        $permission_task = $admin_tasks[$permission_key];
98
        unset($admin_tasks[$permission_key]);
99
        $admin_tasks[$permission_key] = $permission_task;
100
      }
101
102
      $menu_items[$info->info['name']] = array($info->info['description'], $admin_tasks);
103
    }
104
  }
105
  return theme('system_admin_index', array('menu_items' => $menu_items));
106
}
107
108
/**
109
 * Displays the configuration overview page.
110
 *
111
 * This menu callback implementation is a legacy function that used to display
112
 * the configuration overview page at admin/config. It is currently unused and
113
 * will be removed in Drupal 8. The page at admin/config is now generated by
114
 * system_admin_config_page().
115
 *
116
 * @deprecated
117
 * @see system_admin_config_page()
118
 */
119
function system_settings_overview() {
120
  // Check database setup if necessary
121
  if (function_exists('db_check_setup') && empty($_POST)) {
122
    db_check_setup();
123
  }
124
125
  $item = menu_get_item('admin/config');
126
  $content = system_admin_menu_block($item);
127
128
  $output = theme('admin_block_content', array('content' => $content));
129
130
  return $output;
131
}
132
133
/**
134
 * Menu callback; displays a listing of all themes.
135
 */
136
function system_themes_page() {
137
  // Get current list of themes.
138
  $themes = system_rebuild_theme_data();
139
  uasort($themes, 'system_sort_modules_by_info_name');
140
141
  $theme_default = variable_get('theme_default', 'bartik');
142
  $theme_groups  = array();
143
144
  foreach ($themes as &$theme) {
145
    if (!empty($theme->info['hidden'])) {
146
      continue;
147
    }
148
    $admin_theme_options[$theme->name] = $theme->info['name'];
149
    $theme->is_default = ($theme->name == $theme_default);
150
151
    // Identify theme screenshot.
152
    $theme->screenshot = NULL;
153
    // Create a list which includes the current theme and all its base themes.
154
    if (isset($themes[$theme->name]->base_themes)) {
155
      $theme_keys = array_keys($themes[$theme->name]->base_themes);
156
      $theme_keys[] = $theme->name;
157
    }
158
    else {
159
      $theme_keys = array($theme->name);
160
    }
161
    // Look for a screenshot in the current theme or in its closest ancestor.
162
    foreach (array_reverse($theme_keys) as $theme_key) {
163
      if (isset($themes[$theme_key]) && file_exists($themes[$theme_key]->info['screenshot'])) {
164
        $theme->screenshot = array(
165
          'path' => $themes[$theme_key]->info['screenshot'],
166
          'alt' => t('Screenshot for !theme theme', array('!theme' => $theme->info['name'])),
167
          'title' => t('Screenshot for !theme theme', array('!theme' => $theme->info['name'])),
168
          'attributes' => array('class' => array('screenshot')),
169
        );
170
        break;
171
      }
172
    }
173
174
    if (empty($theme->status)) {
175
     // Ensure this theme is compatible with this version of core.
176
     // Require the 'content' region to make sure the main page
177
     // content has a common place in all themes.
178
      $theme->incompatible_core = !isset($theme->info['core']) || ($theme->info['core'] != DRUPAL_CORE_COMPATIBILITY) || (!isset($theme->info['regions']['content']));
179
      $theme->incompatible_php = version_compare(phpversion(), $theme->info['php']) < 0;
180
    }
181
    $query['token'] = drupal_get_token('system-theme-operation-link');
182
    $theme->operations = array();
183
    if (!empty($theme->status) || !$theme->incompatible_core && !$theme->incompatible_php) {
184
      // Create the operations links.
185
      $query['theme'] = $theme->name;
186
      if (drupal_theme_access($theme)) {
187
        $theme->operations[] = array(
188
          'title' => t('Settings'),
189
          'href' => 'admin/appearance/settings/' . $theme->name,
190
          'attributes' => array('title' => t('Settings for !theme theme', array('!theme' => $theme->info['name']))),
191
        );
192
      }
193
      if (!empty($theme->status)) {
194
        if (!$theme->is_default) {
195
          $theme->operations[] = array(
196
            'title' => t('Disable'),
197
            'href' => 'admin/appearance/disable',
198
            'query' => $query,
199
            'attributes' => array('title' => t('Disable !theme theme', array('!theme' => $theme->info['name']))),
200
          );
201
          $theme->operations[] = array(
202
            'title' => t('Set default'),
203
            'href' => 'admin/appearance/default',
204
            'query' => $query,
205
            'attributes' => array('title' => t('Set !theme as default theme', array('!theme' => $theme->info['name']))),
206
          );
207
        }
208
      }
209
      else {
210
        $theme->operations[] = array(
211
          'title' => t('Enable'),
212
          'href' => 'admin/appearance/enable',
213
          'query' => $query,
214
          'attributes' => array('title' => t('Enable !theme theme', array('!theme' => $theme->info['name']))),
215
        );
216
        $theme->operations[] = array(
217
          'title' => t('Enable and set default'),
218
          'href' => 'admin/appearance/default',
219
          'query' => $query,
220
          'attributes' => array('title' => t('Enable !theme as default theme', array('!theme' => $theme->info['name']))),
221
        );
222
      }
223
    }
224
225
    // Add notes to default and administration theme.
226
    $theme->notes = array();
227
    $theme->classes = array();
228
    if ($theme->is_default) {
229
      $theme->classes[] = 'theme-default';
230
      $theme->notes[] = t('default theme');
231
    }
232
233
    // Sort enabled and disabled themes into their own groups.
234
    $theme_groups[$theme->status ? 'enabled' : 'disabled'][] = $theme;
235
  }
236
237
  // There are two possible theme groups.
238
  $theme_group_titles = array(
239
    'enabled' => format_plural(count($theme_groups['enabled']), 'Enabled theme', 'Enabled themes'),
240
  );
241
  if (!empty($theme_groups['disabled'])) {
242
    $theme_group_titles['disabled'] = format_plural(count($theme_groups['disabled']), 'Disabled theme', 'Disabled themes');
243
  }
244
245
  uasort($theme_groups['enabled'], 'system_sort_themes');
246
  drupal_alter('system_themes_page', $theme_groups);
247
248
  $admin_form = drupal_get_form('system_themes_admin_form', $admin_theme_options);
249
  return theme('system_themes_page', array('theme_groups' => $theme_groups, 'theme_group_titles' => $theme_group_titles)) . drupal_render($admin_form);
250
}
251
252
/**
253
 * Form to select the administration theme.
254
 *
255
 * @ingroup forms
256
 * @see system_themes_admin_form_submit()
257
 */
258
function system_themes_admin_form($form, &$form_state, $theme_options) {
259
  // Administration theme settings.
260
  $form['admin_theme'] = array(
261
    '#type' => 'fieldset',
262
    '#title' => t('Administration theme'),
263
  );
264
  $form['admin_theme']['admin_theme'] = array(
265
    '#type' => 'select',
266
    '#options' => array(0 => t('Default theme')) + $theme_options,
267
    '#title' => t('Administration theme'),
268
    '#description' => t('Choose "Default theme" to always use the same theme as the rest of the site.'),
269
    '#default_value' => variable_get('admin_theme', 0),
270
  );
271
  $form['admin_theme']['node_admin_theme'] = array(
272
    '#type' => 'checkbox',
273
    '#title' => t('Use the administration theme when editing or creating content'),
274
    '#default_value' => variable_get('node_admin_theme', '0'),
275
  );
276
  $form['admin_theme']['actions'] = array('#type' => 'actions');
277
  $form['admin_theme']['actions']['submit'] = array(
278
    '#type' => 'submit',
279
    '#value' => t('Save configuration'),
280
  );
281
  return $form;
282
}
283
284
/**
285
 * Process system_themes_admin_form form submissions.
286
 */
287
function system_themes_admin_form_submit($form, &$form_state) {
288
  drupal_set_message(t('The configuration options have been saved.'));
289
  variable_set('admin_theme', $form_state['values']['admin_theme']);
290
  variable_set('node_admin_theme', $form_state['values']['node_admin_theme']);
291
}
292
293
/**
294
 * Menu callback; Enables a theme.
295
 */
296
function system_theme_enable() {
297
  if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) {
298
    $theme = $_REQUEST['theme'];
299
    // Get current list of themes.
300
    $themes = list_themes();
301
302
    // Check if the specified theme is one recognized by the system.
303
    if (!empty($themes[$theme])) {
304
      theme_enable(array($theme));
305
      drupal_set_message(t('The %theme theme has been enabled.', array('%theme' => $themes[$theme]->info['name'])));
306
    }
307
    else {
308
      drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
309
    }
310
    drupal_goto('admin/appearance');
311
  }
312
  return MENU_ACCESS_DENIED;
313
}
314
315
/**
316
 * Menu callback; Disables a theme.
317
 */
318
function system_theme_disable() {
319
  if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) {
320
    $theme = $_REQUEST['theme'];
321
    // Get current list of themes.
322
    $themes = list_themes();
323
324
    // Check if the specified theme is one recognized by the system.
325
    if (!empty($themes[$theme])) {
326
      if ($theme == variable_get('theme_default', 'bartik')) {
327
        // Don't disable the default theme.
328
        drupal_set_message(t('%theme is the default theme and cannot be disabled.', array('%theme' => $themes[$theme]->info['name'])), 'error');
329
      }
330
      else {
331
        theme_disable(array($theme));
332
        drupal_set_message(t('The %theme theme has been disabled.', array('%theme' => $themes[$theme]->info['name'])));
333
      }
334
    }
335
    else {
336
      drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
337
    }
338
    drupal_goto('admin/appearance');
339
  }
340
  return MENU_ACCESS_DENIED;
341
}
342
343
/**
344
 * Menu callback; Set the default theme.
345
 */
346
function system_theme_default() {
347
  if (isset($_REQUEST['theme']) && isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'system-theme-operation-link')) {
348
    $theme = $_REQUEST['theme'];
349
    // Get current list of themes.
350
    $themes = list_themes();
351
352
    // Check if the specified theme is one recognized by the system.
353
    if (!empty($themes[$theme])) {
354
      // Enable the theme if it is currently disabled.
355
      if (empty($themes[$theme]->status)) {
356
       theme_enable(array($theme));
357
      }
358
      // Set the default theme.
359
      variable_set('theme_default', $theme);
360
361
      // Rebuild the menu. This duplicates the menu_rebuild() in theme_enable().
362
      // However, modules must know the current default theme in order to use
363
      // this information in hook_menu() or hook_menu_alter() implementations,
364
      // and doing the variable_set() before the theme_enable() could result
365
      // in a race condition where the theme is default but not enabled.
366
      menu_rebuild();
367
368
      // The status message depends on whether an admin theme is currently in use:
369
      // a value of 0 means the admin theme is set to be the default theme.
370
      $admin_theme = variable_get('admin_theme', 0);
371
      if ($admin_theme != 0 && $admin_theme != $theme) {
372
        drupal_set_message(t('Please note that the administration theme is still set to the %admin_theme theme; consequently, the theme on this page remains unchanged. All non-administrative sections of the site, however, will show the selected %selected_theme theme by default.', array(
373
          '%admin_theme' => $themes[$admin_theme]->info['name'],
374
          '%selected_theme' => $themes[$theme]->info['name'],
375
        )));
376
      }
377
      else {
378
        drupal_set_message(t('%theme is now the default theme.', array('%theme' => $themes[$theme]->info['name'])));
379
      }
380
    }
381
    else {
382
      drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
383
    }
384
    drupal_goto('admin/appearance');
385
  }
386
  return MENU_ACCESS_DENIED;
387
}
388
389
/**
390
 * Form builder; display theme configuration for entire site and individual themes.
391
 *
392
 * @param $key
393
 *   A theme name.
394
 * @return
395
 *   The form structure.
396
 * @ingroup forms
397
 * @see system_theme_settings_submit()
398
 */
399
function system_theme_settings($form, &$form_state, $key = '') {
400
  // Default settings are defined in theme_get_setting() in includes/theme.inc
401
  if ($key) {
402
    $var = 'theme_' . $key . '_settings';
403
    $themes = list_themes();
404
    $features = $themes[$key]->info['features'];
405
  }
406
  else {
407
    $var = 'theme_settings';
408
  }
409
410
  $form['var'] = array('#type' => 'hidden', '#value' => $var);
411
412
  // Toggle settings
413
  $toggles = array(
414
    'logo'                      => t('Logo'),
415
    'name'                      => t('Site name'),
416
    'slogan'                    => t('Site slogan'),
417
    'node_user_picture'         => t('User pictures in posts'),
418
    'comment_user_picture'      => t('User pictures in comments'),
419
    'comment_user_verification' => t('User verification status in comments'),
420
    'favicon'                   => t('Shortcut icon'),
421
    'main_menu'                 => t('Main menu'),
422
    'secondary_menu'            => t('Secondary menu'),
423
  );
424
425
  // Some features are not always available
426
  $disabled = array();
427
  if (!variable_get('user_pictures', 0)) {
428
    $disabled['toggle_node_user_picture'] = TRUE;
429
    $disabled['toggle_comment_user_picture'] = TRUE;
430
  }
431
  if (!module_exists('comment')) {
432
    $disabled['toggle_comment_user_picture'] = TRUE;
433
    $disabled['toggle_comment_user_verification'] = TRUE;
434
  }
435
436
  $form['theme_settings'] = array(
437
    '#type' => 'fieldset',
438
    '#title' => t('Toggle display'),
439
    '#description' => t('Enable or disable the display of certain page elements.'),
440
  );
441
  foreach ($toggles as $name => $title) {
442
    if ((!$key) || in_array($name, $features)) {
443
      $form['theme_settings']['toggle_' . $name] = array('#type' => 'checkbox', '#title' => $title, '#default_value' => theme_get_setting('toggle_' . $name, $key));
444
      // Disable checkboxes for features not supported in the current configuration.
445
      if (isset($disabled['toggle_' . $name])) {
446
        $form['theme_settings']['toggle_' . $name]['#disabled'] = TRUE;
447
      }
448
    }
449
  }
450
451
  if (!element_children($form['theme_settings'])) {
452
    // If there is no element in the theme settings fieldset then do not show
453
    // it -- but keep it in the form if another module wants to alter.
454
    $form['theme_settings']['#access'] = FALSE;
455
  }
456
457
  // Logo settings
458
  if ((!$key) || in_array('logo', $features)) {
459
    $form['logo'] = array(
460
      '#type' => 'fieldset',
461
      '#title' => t('Logo image settings'),
462
      '#description' => t('If toggled on, the following logo will be displayed.'),
463
      '#attributes' => array('class' => array('theme-settings-bottom')),
464
    );
465
    $form['logo']['default_logo'] = array(
466
      '#type' => 'checkbox',
467
      '#title' => t('Use the default logo'),
468
      '#default_value' => theme_get_setting('default_logo', $key),
469
      '#tree' => FALSE,
470
      '#description' => t('Check here if you want the theme to use the logo supplied with it.')
471
    );
472
    $form['logo']['settings'] = array(
473
      '#type' => 'container',
474
      '#states' => array(
475
        // Hide the logo settings when using the default logo.
476
        'invisible' => array(
477
          'input[name="default_logo"]' => array('checked' => TRUE),
478
        ),
479
      ),
480
    );
481
    $form['logo']['settings']['logo_path'] = array(
482
      '#type' => 'textfield',
483
      '#title' => t('Path to custom logo'),
484
      '#description' => t('The path to the file you would like to use as your logo file instead of the default logo.'),
485
      '#default_value' => theme_get_setting('logo_path', $key),
486
    );
487
    $form['logo']['settings']['logo_upload'] = array(
488
      '#type' => 'file',
489
      '#title' => t('Upload logo image'),
490
      '#maxlength' => 40,
491
      '#description' => t("If you don't have direct file access to the server, use this field to upload your logo.")
492
    );
493
  }
494
495
  if ((!$key) || in_array('favicon', $features)) {
496
    $form['favicon'] = array(
497
      '#type' => 'fieldset',
498
      '#title' => t('Shortcut icon settings'),
499
      '#description' => t("Your shortcut icon, or 'favicon', is displayed in the address bar and bookmarks of most browsers."),
500
    );
501
    $form['favicon']['default_favicon'] = array(
502
      '#type' => 'checkbox',
503
      '#title' => t('Use the default shortcut icon.'),
504
      '#default_value' => theme_get_setting('default_favicon', $key),
505
      '#description' => t('Check here if you want the theme to use the default shortcut icon.')
506
    );
507
    $form['favicon']['settings'] = array(
508
      '#type' => 'container',
509
      '#states' => array(
510
        // Hide the favicon settings when using the default favicon.
511
        'invisible' => array(
512
          'input[name="default_favicon"]' => array('checked' => TRUE),
513
        ),
514
      ),
515
    );
516
    $form['favicon']['settings']['favicon_path'] = array(
517
      '#type' => 'textfield',
518
      '#title' => t('Path to custom icon'),
519
      '#description' => t('The path to the image file you would like to use as your custom shortcut icon.'),
520
      '#default_value' => theme_get_setting('favicon_path', $key),
521
    );
522
    $form['favicon']['settings']['favicon_upload'] = array(
523
      '#type' => 'file',
524
      '#title' => t('Upload icon image'),
525
      '#description' => t("If you don't have direct file access to the server, use this field to upload your shortcut icon.")
526
    );
527
  }
528
529
  // Inject human-friendly values for logo and favicon.
530
  foreach (array('logo' => 'logo.png', 'favicon' => 'favicon.ico') as $type => $default) {
531
    if (isset($form[$type]['settings'][$type . '_path'])) {
532
      $element = &$form[$type]['settings'][$type . '_path'];
533
534
      // If path is a public:// URI, display the path relative to the files
535
      // directory; stream wrappers are not end-user friendly.
536
      $original_path = $element['#default_value'];
537
      $friendly_path = NULL;
538
      if (file_uri_scheme($original_path) == 'public') {
539
        $friendly_path = file_uri_target($original_path);
540
        $element['#default_value'] = $friendly_path;
541
      }
542
    }
543
  }
544
545
  if ($key) {
546
    // Call engine-specific settings.
547
    $function = $themes[$key]->prefix . '_engine_settings';
548
    if (function_exists($function)) {
549
      $form['engine_specific'] = array(
550
        '#type' => 'fieldset',
551
        '#title' => t('Theme-engine-specific settings'),
552
        '#description' => t('These settings only exist for the themes based on the %engine theme engine.', array('%engine' => $themes[$key]->prefix)),
553
      );
554
      $function($form, $form_state);
555
    }
556
557
    // Create a list which includes the current theme and all its base themes.
558
    if (isset($themes[$key]->base_themes)) {
559
      $theme_keys = array_keys($themes[$key]->base_themes);
560
      $theme_keys[] = $key;
561
    }
562
    else {
563
      $theme_keys = array($key);
564
    }
565
566
    // Save the name of the current theme (if any), so that we can temporarily
567
    // override the current theme and allow theme_get_setting() to work
568
    // without having to pass the theme name to it.
569
    $default_theme = !empty($GLOBALS['theme_key']) ? $GLOBALS['theme_key'] : NULL;
570
    $GLOBALS['theme_key'] = $key;
571
572
    // Process the theme and all its base themes.
573
    foreach ($theme_keys as $theme) {
574
      // Include the theme-settings.php file.
575
      $filename = DRUPAL_ROOT . '/' . str_replace("/$theme.info", '', $themes[$theme]->filename) . '/theme-settings.php';
576
      if (file_exists($filename)) {
577
        require_once $filename;
578
      }
579
580
      // Call theme-specific settings.
581
      $function = $theme . '_form_system_theme_settings_alter';
582
      if (function_exists($function)) {
583
        $function($form, $form_state);
584
      }
585
    }
586
587
    // Restore the original current theme.
588
    if (isset($default_theme)) {
589
      $GLOBALS['theme_key'] = $default_theme;
590
    }
591
    else {
592
      unset($GLOBALS['theme_key']);
593
    }
594
  }
595
596
  $form = system_settings_form($form);
597
  // We don't want to call system_settings_form_submit(), so change #submit.
598
  array_pop($form['#submit']);
599
  $form['#submit'][] = 'system_theme_settings_submit';
600
  $form['#validate'][] = 'system_theme_settings_validate';
601
  return $form;
602
}
603
604
/**
605
 * Validator for the system_theme_settings() form.
606
 */
607
function system_theme_settings_validate($form, &$form_state) {
608
  // Handle file uploads.
609
  $validators = array('file_validate_is_image' => array());
610
611
  // Check for a new uploaded logo.
612
  $file = file_save_upload('logo_upload', $validators);
613
  if (isset($file)) {
614
    // File upload was attempted.
615
    if ($file) {
616
      // Put the temporary file in form_values so we can save it on submit.
617
      $form_state['values']['logo_upload'] = $file;
618
    }
619
    else {
620
      // File upload failed.
621
      form_set_error('logo_upload', t('The logo could not be uploaded.'));
622
    }
623
  }
624
625
  $validators = array('file_validate_extensions' => array('ico png gif jpg jpeg apng svg'));
626
627
  // Check for a new uploaded favicon.
628
  $file = file_save_upload('favicon_upload', $validators);
629
  if (isset($file)) {
630
    // File upload was attempted.
631
    if ($file) {
632
      // Put the temporary file in form_values so we can save it on submit.
633
      $form_state['values']['favicon_upload'] = $file;
634
    }
635
    else {
636
      // File upload failed.
637
      form_set_error('favicon_upload', t('The favicon could not be uploaded.'));
638
    }
639
  }
640
641
  // If the user provided a path for a logo or favicon file, make sure a file
642
  // exists at that path.
643
  if ($form_state['values']['logo_path']) {
644
    $path = _system_theme_settings_validate_path($form_state['values']['logo_path']);
645
    if (!$path) {
646
      form_set_error('logo_path', t('The custom logo path is invalid.'));
647
    }
648
  }
649
  if ($form_state['values']['favicon_path']) {
650
    $path = _system_theme_settings_validate_path($form_state['values']['favicon_path']);
651
    if (!$path) {
652
      form_set_error('favicon_path', t('The custom favicon path is invalid.'));
653
    }
654
  }
655
}
656
657
/**
658
 * Helper function for the system_theme_settings form.
659
 *
660
 * Attempts to validate normal system paths, paths relative to the public files
661
 * directory, or stream wrapper URIs. If the given path is any of the above,
662
 * returns a valid path or URI that the theme system can display.
663
 *
664
 * @param $path
665
 *   A path relative to the Drupal root or to the public files directory, or
666
 *   a stream wrapper URI.
667
 * @return mixed
668
 *   A valid path that can be displayed through the theme system, or FALSE if
669
 *   the path could not be validated.
670
 */
671
function _system_theme_settings_validate_path($path) {
672
  // Absolute local file paths are invalid.
673
  if (drupal_realpath($path) == $path) {
674
    return FALSE;
675
  }
676
  // A path relative to the Drupal root or a fully qualified URI is valid.
677
  if (is_file($path)) {
678
    return $path;
679
  }
680
  // Prepend 'public://' for relative file paths within public filesystem.
681
  if (file_uri_scheme($path) === FALSE) {
682
    $path = 'public://' . $path;
683
  }
684
  if (is_file($path)) {
685
    return $path;
686
  }
687
  return FALSE;
688
}
689
690
/**
691
 * Process system_theme_settings form submissions.
692
 */
693
function system_theme_settings_submit($form, &$form_state) {
694
  // Exclude unnecessary elements before saving.
695
  form_state_values_clean($form_state);
696
697
  $values = $form_state['values'];
698
699
  // Extract the name of the theme from the submitted form values, then remove
700
  // it from the array so that it is not saved as part of the variable.
701
  $key = $values['var'];
702
  unset($values['var']);
703
704
  // If the user uploaded a new logo or favicon, save it to a permanent location
705
  // and use it in place of the default theme-provided file.
706
  if ($file = $values['logo_upload']) {
707
    unset($values['logo_upload']);
708
    $filename = file_unmanaged_copy($file->uri);
709
    $values['default_logo'] = 0;
710
    $values['logo_path'] = $filename;
711
    $values['toggle_logo'] = 1;
712
  }
713
  if ($file = $values['favicon_upload']) {
714
    unset($values['favicon_upload']);
715
    $filename = file_unmanaged_copy($file->uri);
716
    $values['default_favicon'] = 0;
717
    $values['favicon_path'] = $filename;
718
    $values['toggle_favicon'] = 1;
719
  }
720
721
  // If the user entered a path relative to the system files directory for
722
  // a logo or favicon, store a public:// URI so the theme system can handle it.
723
  if (!empty($values['logo_path'])) {
724
    $values['logo_path'] = _system_theme_settings_validate_path($values['logo_path']);
725
  }
726
  if (!empty($values['favicon_path'])) {
727
    $values['favicon_path'] = _system_theme_settings_validate_path($values['favicon_path']);
728
  }
729
730
  if (empty($values['default_favicon']) && !empty($values['favicon_path'])) {
731
    $values['favicon_mimetype'] = file_get_mimetype($values['favicon_path']);
732
  }
733
734
  variable_set($key, $values);
735
  drupal_set_message(t('The configuration options have been saved.'));
736
737
  cache_clear_all();
738
}
739
740
/**
741
 * Recursively check compatibility.
742
 *
743
 * @param $incompatible
744
 *   An associative array which at the end of the check contains all
745
 *   incompatible files as the keys, their values being TRUE.
746
 * @param $files
747
 *   The set of files that will be tested.
748
 * @param $file
749
 *   The file at which the check starts.
750
 * @return
751
 *   Returns TRUE if an incompatible file is found, NULL (no return value)
752
 *   otherwise.
753
 */
754
function _system_is_incompatible(&$incompatible, $files, $file) {
755
  if (isset($incompatible[$file->name])) {
756
    return TRUE;
757
  }
758
  // Recursively traverse required modules, looking for incompatible modules.
759
  foreach ($file->requires as $requires) {
760
    if (isset($files[$requires]) && _system_is_incompatible($incompatible, $files, $files[$requires])) {
761
      $incompatible[$file->name] = TRUE;
762
      return TRUE;
763
    }
764
  }
765
}
766
767
/**
768
 * Menu callback; provides module enable/disable interface.
769
 *
770
 * The list of modules gets populated by module.info files, which contain each
771
 * module's name, description, and information about which modules it requires.
772
 * See drupal_parse_info_file() for information on module.info descriptors.
773
 *
774
 * Dependency checking is performed to ensure that a module:
775
 * - can not be enabled if there are disabled modules it requires.
776
 * - can not be disabled if there are enabled modules which depend on it.
777
 *
778
 * @param $form_state
779
 *   An associative array containing the current state of the form.
780
 *
781
 * @return
782
 *   The form array.
783
 *
784
 * @ingroup forms
785
 * @see theme_system_modules()
786
 * @see system_modules_submit()
787
 */
788
function system_modules($form, $form_state = array()) {
789
  // Get current list of modules.
790
  $files = system_rebuild_module_data();
791
792
  // Remove hidden modules from display list.
793
  $visible_files = $files;
794
  foreach ($visible_files as $filename => $file) {
795
    if (!empty($file->info['hidden'])) {
796
      unset($visible_files[$filename]);
797
    }
798
  }
799
800
  uasort($visible_files, 'system_sort_modules_by_info_name');
801
802
  // If the modules form was submitted, then system_modules_submit() runs first
803
  // and if there are unfilled required modules, then $form_state['storage'] is
804
  // filled, triggering a rebuild. In this case we need to display a
805
  // confirmation form.
806
  if (!empty($form_state['storage'])) {
807
    return system_modules_confirm_form($visible_files, $form_state['storage']);
808
  }
809
810
  $modules = array();
811
  $form['modules'] = array('#tree' => TRUE);
812
813
  // Used when checking if module implements a help page.
814
  $help_arg = module_exists('help') ? drupal_help_arg() : FALSE;
815
816
  // Used when displaying modules that are required by the installation profile.
817
  require_once DRUPAL_ROOT . '/includes/install.inc';
818
  $distribution_name = check_plain(drupal_install_profile_distribution_name());
819
820
  // Iterate through each of the modules.
821
  foreach ($visible_files as $filename => $module) {
822
    $extra = array();
823
    $extra['enabled'] = (bool) $module->status;
824
    if (!empty($module->info['required'] )) {
825
      $extra['disabled'] = TRUE;
826
      $extra['required_by'][] = $distribution_name . (!empty($module->info['explanation']) ? ' ('. $module->info['explanation'] .')' : '');
827
    }
828
829
    // If this module requires other modules, add them to the array.
830
    foreach ($module->requires as $requires => $v) {
831
      if (!isset($files[$requires])) {
832
        $extra['requires'][$requires] = t('@module (<span class="admin-missing">missing</span>)', array('@module' => drupal_ucfirst($requires)));
833
        $extra['disabled'] = TRUE;
834
      }
835
      // Only display visible modules.
836
      elseif (isset($visible_files[$requires])) {
837
        $requires_name = $files[$requires]->info['name'];
838
        // Disable this module if it is incompatible with the dependency's version.
839
        if ($incompatible_version = drupal_check_incompatibility($v, str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $files[$requires]->info['version']))) {
840
          $extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> version @version)', array(
841
            '@module' => $requires_name . $incompatible_version,
842
            '@version' => $files[$requires]->info['version'],
843
          ));
844
          $extra['disabled'] = TRUE;
845
        }
846
        // Disable this module if the dependency is incompatible with this
847
        // version of Drupal core.
848
        elseif ($files[$requires]->info['core'] != DRUPAL_CORE_COMPATIBILITY) {
849
          $extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> this version of Drupal core)', array(
850
            '@module' => $requires_name,
851
          ));
852
          $extra['disabled'] = TRUE;
853
        }
854
        elseif ($files[$requires]->status) {
855
          $extra['requires'][$requires] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $requires_name));
856
        }
857
        else {
858
          $extra['requires'][$requires] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $requires_name));
859
        }
860
      }
861
    }
862
    // Generate link for module's help page, if there is one.
863
    if ($help_arg && $module->status && in_array($filename, module_implements('help'))) {
864
      if (module_invoke($filename, 'help', "admin/help#$filename", $help_arg)) {
865
        $extra['links']['help'] = array(
866
          '#type' => 'link',
867
          '#title' => t('Help'),
868
          '#href' => "admin/help/$filename",
869
          '#options' => array('attributes' => array('class' =>  array('module-link', 'module-link-help'), 'title' => t('Help'))),
870
        );
871
      }
872
    }
873
    // Generate link for module's permission, if the user has access to it.
874
    if ($module->status && user_access('administer permissions') && in_array($filename, module_implements('permission'))) {
875
      $extra['links']['permissions'] = array(
876
        '#type' => 'link',
877
        '#title' => t('Permissions'),
878
        '#href' => 'admin/people/permissions',
879
        '#options' => array('fragment' => 'module-' . $filename, 'attributes' => array('class' => array('module-link', 'module-link-permissions'), 'title' => t('Configure permissions'))),
880
      );
881
    }
882
    // Generate link for module's configuration page, if the module provides
883
    // one.
884
    if ($module->status && isset($module->info['configure'])) {
885
      $configure_link = menu_get_item($module->info['configure']);
886
      if ($configure_link['access']) {
887
        $extra['links']['configure'] = array(
888
          '#type' => 'link',
889
          '#title' => t('Configure'),
890
          '#href' => $configure_link['href'],
891
          '#options' => array('attributes' => array('class' => array('module-link', 'module-link-configure'), 'title' => $configure_link['description'])),
892
        );
893
      }
894
    }
895
896
    // If this module is required by other modules, list those, and then make it
897
    // impossible to disable this one.
898
    foreach ($module->required_by as $required_by => $v) {
899
      // Hidden modules are unset already.
900
      if (isset($visible_files[$required_by])) {
901
        if ($files[$required_by]->status == 1 && $module->status == 1) {
902
          $extra['required_by'][] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $files[$required_by]->info['name']));
903
          $extra['disabled'] = TRUE;
904
        }
905
        else {
906
          $extra['required_by'][] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $files[$required_by]->info['name']));
907
        }
908
      }
909
    }
910
    $form['modules'][$module->info['package']][$filename] = _system_modules_build_row($module->info, $extra);
911
  }
912
913
  // Add basic information to the fieldsets.
914
  foreach (element_children($form['modules']) as $package) {
915
    $form['modules'][$package] += array(
916
      '#type' => 'fieldset',
917
      '#title' => t($package),
918
      '#collapsible' => TRUE,
919
      '#theme' => 'system_modules_fieldset',
920
      '#header' => array(
921
        array('data' => t('Enabled'), 'class' => array('checkbox')),
922
        t('Name'),
923
        t('Version'),
924
        t('Description'),
925
        array('data' => t('Operations'), 'colspan' => 3),
926
      ),
927
      // Ensure that the "Core" package fieldset comes first.
928
      '#weight' => $package == 'Core' ? -10 : NULL,
929
    );
930
  }
931
932
  // Lastly, sort all fieldsets by title.
933
  uasort($form['modules'], 'element_sort_by_title');
934
935
  $form['actions'] = array('#type' => 'actions');
936
  $form['actions']['submit'] = array(
937
    '#type' => 'submit',
938
    '#value' => t('Save configuration'),
939
  );
940
  $form['#action'] = url('admin/modules/list/confirm');
941
942
  return $form;
943
}
944
945
/**
946
 * Array sorting callback; sorts modules or themes by their name.
947
 */
948
function system_sort_modules_by_info_name($a, $b) {
949
  return strcasecmp($a->info['name'], $b->info['name']);
950
}
951
952
/**
953 e33d3026 Julien Enselme
 * Sorts themes by their names, with the default theme listed first.
954
 *
955
 * Callback for uasort() within system_themes_page().
956
 *
957
 * @see system_sort_modules_by_info_name().
958 85ad3d82 Assos Assos
 */
959
function system_sort_themes($a, $b) {
960
  if ($a->is_default) {
961
    return -1;
962
  }
963
  if ($b->is_default) {
964
    return 1;
965
  }
966
  return strcasecmp($a->info['name'], $b->info['name']);
967
}
968
969
/**
970
 * Build a table row for the system modules page.
971
 */
972
function _system_modules_build_row($info, $extra) {
973
  // Add in the defaults.
974
  $extra += array(
975
    'requires' => array(),
976
    'required_by' => array(),
977
    'disabled' => FALSE,
978
    'enabled' => FALSE,
979
    'links' => array(),
980
  );
981
  $form = array(
982
    '#tree' => TRUE,
983
  );
984
  // Set the basic properties.
985
  $form['name'] = array(
986
    '#markup' => $info['name'],
987
  );
988
  $form['description'] = array(
989
    '#markup' => t($info['description']),
990
  );
991
  $form['version'] = array(
992
    '#markup' => $info['version'],
993
  );
994
  $form['#requires'] = $extra['requires'];
995
  $form['#required_by'] = $extra['required_by'];
996
997
  // Check the compatibilities.
998
  $compatible = TRUE;
999
  $status_short = '';
1000
  $status_long = '';
1001
1002 4444412d Julien Enselme
  // Initialize empty arrays of long and short reasons explaining why the
1003
  // module is incompatible.
1004
  // Add each reason as a separate element in both the arrays.
1005
  $reasons_short = array();
1006
  $reasons_long = array();
1007
1008 85ad3d82 Assos Assos
  // Check the core compatibility.
1009
  if (!isset($info['core']) || $info['core'] != DRUPAL_CORE_COMPATIBILITY) {
1010
    $compatible = FALSE;
1011 4444412d Julien Enselme
    $reasons_short[] = t('Incompatible with this version of Drupal core.');
1012
    $reasons_long[] = t('This version is not compatible with Drupal !core_version and should be replaced.', array('!core_version' => DRUPAL_CORE_COMPATIBILITY));
1013 85ad3d82 Assos Assos
  }
1014
1015
  // Ensure this module is compatible with the currently installed version of PHP.
1016
  if (version_compare(phpversion(), $info['php']) < 0) {
1017
    $compatible = FALSE;
1018 4444412d Julien Enselme
    $reasons_short[] = t('Incompatible with this version of PHP');
1019 85ad3d82 Assos Assos
    $php_required = $info['php'];
1020
    if (substr_count($info['php'], '.') < 2) {
1021
      $php_required .= '.*';
1022
    }
1023 4444412d Julien Enselme
    $reasons_long[] = t('This module requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $php_required, '!php_version' => phpversion()));
1024 85ad3d82 Assos Assos
  }
1025
1026
  // If this module is compatible, present a checkbox indicating
1027
  // this module may be installed. Otherwise, show a big red X.
1028
  if ($compatible) {
1029
    $form['enable'] = array(
1030
      '#type' => 'checkbox',
1031
      '#title' => t('Enable'),
1032
      '#default_value' => $extra['enabled'],
1033
    );
1034
    if ($extra['disabled']) {
1035
      $form['enable']['#disabled'] = TRUE;
1036
    }
1037
  }
1038
  else {
1039 4444412d Julien Enselme
    $status_short = implode(' ', $reasons_short);
1040
    $status_long = implode(' ', $reasons_long);
1041 85ad3d82 Assos Assos
    $form['enable'] = array(
1042
      '#markup' =>  theme('image', array('path' => 'misc/watchdog-error.png', 'alt' => $status_short, 'title' => $status_short)),
1043
    );
1044
    $form['description']['#markup'] .= theme('system_modules_incompatible', array('message' => $status_long));
1045
  }
1046
1047
  // Build operation links.
1048
  foreach (array('help', 'permissions', 'configure') as $key) {
1049
    $form['links'][$key] = (isset($extra['links'][$key]) ? $extra['links'][$key] : array());
1050
  }
1051
1052
  return $form;
1053
}
1054
1055
/**
1056
 * Display confirmation form for required modules.
1057
 *
1058
 * @param $modules
1059
 *   Array of module file objects as returned from system_rebuild_module_data().
1060
 * @param $storage
1061
 *   The contents of $form_state['storage']; an array with two
1062
 *   elements: the list of required modules and the list of status
1063
 *   form field values from the previous screen.
1064
 * @ingroup forms
1065
 */
1066
function system_modules_confirm_form($modules, $storage) {
1067
  $items = array();
1068
1069
  $form['validation_modules'] = array('#type' => 'value', '#value' => $modules);
1070
  $form['status']['#tree'] = TRUE;
1071
1072
  foreach ($storage['more_required'] as $info) {
1073
    $t_argument = array(
1074
      '@module' => $info['name'],
1075
      '@required' => implode(', ', $info['requires']),
1076
    );
1077
    $items[] = format_plural(count($info['requires']), 'You must enable the @required module to install @module.', 'You must enable the @required modules to install @module.', $t_argument);
1078
  }
1079
1080
  foreach ($storage['missing_modules'] as $name => $info) {
1081
    $t_argument = array(
1082
      '@module' => $name,
1083
      '@depends' => implode(', ', $info['depends']),
1084
    );
1085
    $items[] = format_plural(count($info['depends']), 'The @module module is missing, so the following module will be disabled: @depends.', 'The @module module is missing, so the following modules will be disabled: @depends.', $t_argument);
1086
  }
1087
1088
  $form['text'] = array('#markup' => theme('item_list', array('items' => $items)));
1089
1090
  if ($form) {
1091
    // Set some default form values
1092
    $form = confirm_form(
1093
      $form,
1094
      t('Some required modules must be enabled'),
1095
      'admin/modules',
1096
      t('Would you like to continue with the above?'),
1097
      t('Continue'),
1098
      t('Cancel'));
1099
    return $form;
1100
  }
1101
}
1102
1103
/**
1104
 * Submit callback; handles modules form submission.
1105
 */
1106
function system_modules_submit($form, &$form_state) {
1107
  include_once DRUPAL_ROOT . '/includes/install.inc';
1108
1109
  // Builds list of modules.
1110
  $modules = array();
1111
  // If we're not coming from the confirmation form, build the list of modules.
1112
  if (empty($form_state['storage'])) {
1113
    // If we're not coming from the confirmation form, build the module list.
1114
    foreach ($form_state['values']['modules'] as $group_name => $group) {
1115
      foreach ($group as $module => $enabled) {
1116
        $modules[$module] = array('group' => $group_name, 'enabled' => $enabled['enable']);
1117
      }
1118
    }
1119
  }
1120
  else {
1121
    // If we are coming from the confirmation form, fetch
1122
    // the modules out of $form_state.
1123
    $modules = $form_state['storage']['modules'];
1124
  }
1125
1126
  // Collect data for all modules to be able to determine dependencies.
1127
  $files = system_rebuild_module_data();
1128
1129
  // Sorts modules by weight.
1130
  $sort = array();
1131
  foreach (array_keys($modules) as $module) {
1132
    $sort[$module] = $files[$module]->sort;
1133
  }
1134
  array_multisort($sort, $modules);
1135
1136
  // Makes sure all required modules are set to be enabled.
1137
  $more_required = array();
1138
  $missing_modules = array();
1139
  foreach ($modules as $name => $module) {
1140
    if ($module['enabled']) {
1141
      // Checks that all dependencies are set to be enabled.  Stores the ones
1142
      // that are not in $dependencies variable so that the user can be alerted
1143
      // in the confirmation form that more modules need to be enabled.
1144
      $dependencies = array();
1145
      foreach (array_keys($files[$name]->requires) as $required) {
1146
        if (empty($modules[$required]['enabled'])) {
1147
          if (isset($files[$required])) {
1148
            $dependencies[] = $files[$required]->info['name'];
1149
            $modules[$required]['enabled'] = TRUE;
1150
          }
1151
          else {
1152
            $missing_modules[$required]['depends'][] = $name;
1153
            $modules[$name]['enabled'] = FALSE;
1154
          }
1155
        }
1156
      }
1157
1158
      // Stores additional modules that need to be enabled in $more_required.
1159
      if (!empty($dependencies)) {
1160
        $more_required[$name] = array(
1161
          'name' => $files[$name]->info['name'],
1162
          'requires' => $dependencies,
1163
        );
1164
      }
1165
    }
1166
  }
1167
1168
  // Redirects to confirmation form if more modules need to be enabled.
1169
  if ((!empty($more_required) || !empty($missing_modules)) && !isset($form_state['values']['confirm'])) {
1170
    $form_state['storage'] = array(
1171
      'more_required' => $more_required,
1172
      'modules' => $modules,
1173
      'missing_modules' => $missing_modules,
1174
    );
1175
    $form_state['rebuild'] = TRUE;
1176
    return;
1177
  }
1178
1179
  // Invokes hook_requirements('install').  If failures are detected, makes sure
1180
  // the dependent modules aren't installed either.
1181
  foreach ($modules as $name => $module) {
1182
    // Only invoke hook_requirements() on modules that are going to be installed.
1183
    if ($module['enabled'] && drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) {
1184
      if (!drupal_check_module($name)) {
1185
        $modules[$name]['enabled'] = FALSE;
1186
        foreach (array_keys($files[$name]->required_by) as $required_by) {
1187
          $modules[$required_by]['enabled'] = FALSE;
1188
        }
1189
      }
1190
    }
1191
  }
1192
1193
  // Initializes array of actions.
1194
  $actions = array(
1195
    'enable' => array(),
1196
    'disable' => array(),
1197
    'install' => array(),
1198
  );
1199
1200
  // Builds arrays of modules that need to be enabled, disabled, and installed.
1201
  foreach ($modules as $name => $module) {
1202
    if ($module['enabled']) {
1203
      if (drupal_get_installed_schema_version($name) == SCHEMA_UNINSTALLED) {
1204
        $actions['install'][] = $name;
1205
        $actions['enable'][] = $name;
1206
      }
1207
      elseif (!module_exists($name)) {
1208
        $actions['enable'][] = $name;
1209
      }
1210
    }
1211
    elseif (module_exists($name)) {
1212
      $actions['disable'][] = $name;
1213
    }
1214
  }
1215
1216
  // Gets list of modules prior to install process, unsets $form_state['storage']
1217
  // so we don't get redirected back to the confirmation form.
1218
  $pre_install_list = module_list();
1219
  unset($form_state['storage']);
1220
1221
  // Reverse the 'enable' list, to order dependencies before dependents.
1222
  krsort($actions['enable']);
1223
1224
  // Installs, enables, and disables modules.
1225
  module_enable($actions['enable'], FALSE);
1226
  module_disable($actions['disable'], FALSE);
1227
1228
  // Gets module list after install process, flushes caches and displays a
1229
  // message if there are changes.
1230
  $post_install_list = module_list(TRUE);
1231
  if ($pre_install_list != $post_install_list) {
1232
    drupal_flush_all_caches();
1233
    drupal_set_message(t('The configuration options have been saved.'));
1234
  }
1235
1236
  $form_state['redirect'] = 'admin/modules';
1237
}
1238
1239
/**
1240
 * Uninstall functions
1241
 */
1242
1243
/**
1244
 * Builds a form of currently disabled modules.
1245
 *
1246
 * @ingroup forms
1247
 * @see system_modules_uninstall_validate()
1248
 * @see system_modules_uninstall_submit()
1249
 * @param $form_state['values']
1250
 *   Submitted form values.
1251
 * @return
1252
 *   A form array representing the currently disabled modules.
1253
 */
1254
function system_modules_uninstall($form, $form_state = NULL) {
1255
  // Make sure the install API is available.
1256
  include_once DRUPAL_ROOT . '/includes/install.inc';
1257
1258
  // Display the confirm form if any modules have been submitted.
1259
  if (!empty($form_state['storage']) && $confirm_form = system_modules_uninstall_confirm_form($form_state['storage'])) {
1260
    return $confirm_form;
1261
  }
1262
1263
  // Get a list of disabled, installed modules.
1264
  $all_modules = system_rebuild_module_data();
1265
  $disabled_modules = array();
1266
  foreach ($all_modules as $name => $module) {
1267
    if (empty($module->status) && $module->schema_version > SCHEMA_UNINSTALLED) {
1268
      $disabled_modules[$name] = $module;
1269
    }
1270
  }
1271
1272
  // Only build the rest of the form if there are any modules available to
1273
  // uninstall.
1274
  if (!empty($disabled_modules)) {
1275
    $profile = drupal_get_profile();
1276
    uasort($disabled_modules, 'system_sort_modules_by_info_name');
1277
    $form['uninstall'] = array('#tree' => TRUE);
1278
    foreach ($disabled_modules as $module) {
1279
      $module_name = $module->info['name'] ? $module->info['name'] : $module->name;
1280
      $form['modules'][$module->name]['#module_name'] = $module_name;
1281
      $form['modules'][$module->name]['name']['#markup'] = $module_name;
1282
      $form['modules'][$module->name]['description']['#markup'] = t($module->info['description']);
1283
      $form['uninstall'][$module->name] = array(
1284
        '#type' => 'checkbox',
1285
        '#title' => t('Uninstall @module module', array('@module' => $module_name)),
1286
        '#title_display' => 'invisible',
1287
      );
1288
      // All modules which depend on this one must be uninstalled first, before
1289
      // we can allow this module to be uninstalled. (The installation profile
1290
      // is excluded from this list.)
1291
      foreach (array_keys($module->required_by) as $dependent) {
1292
        if ($dependent != $profile && drupal_get_installed_schema_version($dependent) != SCHEMA_UNINSTALLED) {
1293
          $dependent_name = isset($all_modules[$dependent]->info['name']) ? $all_modules[$dependent]->info['name'] : $dependent;
1294
          $form['modules'][$module->name]['#required_by'][] = $dependent_name;
1295
          $form['uninstall'][$module->name]['#disabled'] = TRUE;
1296
        }
1297
      }
1298
    }
1299
    $form['actions'] = array('#type' => 'actions');
1300
    $form['actions']['submit'] = array(
1301
      '#type' => 'submit',
1302
      '#value' => t('Uninstall'),
1303
    );
1304
    $form['#action'] = url('admin/modules/uninstall/confirm');
1305
  }
1306
  else {
1307
    $form['modules'] = array();
1308
  }
1309
1310
  return $form;
1311
}
1312
1313
/**
1314
 * Confirm uninstall of selected modules.
1315
 *
1316
 * @ingroup forms
1317
 * @param $storage
1318
 *   An associative array of modules selected to be uninstalled.
1319
 * @return
1320
 *   A form array representing modules to confirm.
1321
 */
1322
function system_modules_uninstall_confirm_form($storage) {
1323
  // Nothing to build.
1324
  if (empty($storage)) {
1325
    return;
1326
  }
1327
1328
  // Construct the hidden form elements and list items.
1329
  foreach (array_filter($storage['uninstall']) as $module => $value) {
1330
    $info = drupal_parse_info_file(drupal_get_path('module', $module) . '/' . $module . '.info');
1331
    $uninstall[] = $info['name'];
1332
    $form['uninstall'][$module] = array('#type' => 'hidden',
1333
      '#value' => 1,
1334
    );
1335
  }
1336
1337
  // Display a confirm form if modules have been selected.
1338
  if (isset($uninstall)) {
1339
    $form['#confirmed'] = TRUE;
1340
    $form['uninstall']['#tree'] = TRUE;
1341
    $form['modules'] = array('#markup' => '<p>' . t('The following modules will be completely uninstalled from your site, and <em>all data from these modules will be lost</em>!') . '</p>' . theme('item_list', array('items' => $uninstall)));
1342
    $form = confirm_form(
1343
      $form,
1344
      t('Confirm uninstall'),
1345
      'admin/modules/uninstall',
1346
      t('Would you like to continue with uninstalling the above?'),
1347
      t('Uninstall'),
1348
      t('Cancel'));
1349
    return $form;
1350
  }
1351
}
1352
1353
/**
1354
 * Validates the submitted uninstall form.
1355
 */
1356
function system_modules_uninstall_validate($form, &$form_state) {
1357
  // Form submitted, but no modules selected.
1358
  if (!count(array_filter($form_state['values']['uninstall']))) {
1359
    drupal_set_message(t('No modules selected.'), 'error');
1360
    drupal_goto('admin/modules/uninstall');
1361
  }
1362
}
1363
1364
/**
1365
 * Processes the submitted uninstall form.
1366
 */
1367
function system_modules_uninstall_submit($form, &$form_state) {
1368
  // Make sure the install API is available.
1369
  include_once DRUPAL_ROOT . '/includes/install.inc';
1370
1371
  if (!empty($form['#confirmed'])) {
1372
    // Call the uninstall routine for each selected module.
1373
    $modules = array_keys($form_state['values']['uninstall']);
1374
    drupal_uninstall_modules($modules);
1375
    drupal_set_message(t('The selected modules have been uninstalled.'));
1376
1377
    $form_state['redirect'] = 'admin/modules/uninstall';
1378
  }
1379
  else {
1380
    $form_state['storage'] = $form_state['values'];
1381
    $form_state['rebuild'] = TRUE;
1382
  }
1383
}
1384
1385
/**
1386
 * Menu callback. Display blocked IP addresses.
1387
 *
1388
 * @param $default_ip
1389
 *   Optional IP address to be passed on to drupal_get_form() for
1390
 *   use as the default value of the IP address form field.
1391
 */
1392
function system_ip_blocking($default_ip = '') {
1393
  $rows = array();
1394
  $header = array(t('Blocked IP addresses'), t('Operations'));
1395
  $result = db_query('SELECT * FROM {blocked_ips}');
1396
  foreach ($result as $ip) {
1397
    $rows[] = array(
1398
      $ip->ip,
1399
      l(t('delete'), "admin/config/people/ip-blocking/delete/$ip->iid"),
1400
    );
1401
  }
1402
1403
  $build['system_ip_blocking_form'] = drupal_get_form('system_ip_blocking_form', $default_ip);
1404
1405
  $build['system_ip_blocking_table'] = array(
1406
    '#theme' => 'table',
1407
    '#header' => $header,
1408
    '#rows' => $rows,
1409
    '#empty' => t('No blocked IP addresses available.'),
1410
  );
1411
1412
  return $build;
1413
}
1414
1415
/**
1416
 * Define the form for blocking IP addresses.
1417
 *
1418
 * @ingroup forms
1419
 * @see system_ip_blocking_form_validate()
1420
 * @see system_ip_blocking_form_submit()
1421
 */
1422
function system_ip_blocking_form($form, $form_state, $default_ip) {
1423
  $form['ip'] = array(
1424
    '#title' => t('IP address'),
1425
    '#type' => 'textfield',
1426
    '#size' => 48,
1427
    '#maxlength' => 40,
1428
    '#default_value' => $default_ip,
1429
    '#description' => t('Enter a valid IP address.'),
1430
  );
1431
  $form['actions'] = array('#type' => 'actions');
1432
  $form['actions']['submit'] = array(
1433
    '#type' => 'submit',
1434
    '#value' => t('Add'),
1435
  );
1436
  $form['#submit'][] = 'system_ip_blocking_form_submit';
1437
  $form['#validate'][] = 'system_ip_blocking_form_validate';
1438
  return $form;
1439
}
1440
1441
function system_ip_blocking_form_validate($form, &$form_state) {
1442
  $ip = trim($form_state['values']['ip']);
1443
  if (db_query("SELECT * FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField()) {
1444
    form_set_error('ip', t('This IP address is already blocked.'));
1445
  }
1446
  elseif ($ip == ip_address()) {
1447
    form_set_error('ip', t('You may not block your own IP address.'));
1448
  }
1449
  elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE) == FALSE) {
1450
    form_set_error('ip', t('Enter a valid IP address.'));
1451
  }
1452
}
1453
1454
function system_ip_blocking_form_submit($form, &$form_state) {
1455
  $ip = trim($form_state['values']['ip']);
1456
  db_insert('blocked_ips')
1457
    ->fields(array('ip' => $ip))
1458
    ->execute();
1459
  drupal_set_message(t('The IP address %ip has been blocked.', array('%ip' => $ip)));
1460
  $form_state['redirect'] = 'admin/config/people/ip-blocking';
1461
  return;
1462
}
1463
1464
/**
1465
 * IP deletion confirm page.
1466
 *
1467
 * @see system_ip_blocking_delete_submit()
1468
 */
1469
function system_ip_blocking_delete($form, &$form_state, $iid) {
1470
  $form['blocked_ip'] = array(
1471
    '#type' => 'value',
1472
    '#value' => $iid,
1473
  );
1474
  return confirm_form($form, t('Are you sure you want to delete %ip?', array('%ip' => $iid['ip'])), 'admin/config/people/ip-blocking', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
1475
}
1476
1477
/**
1478
 * Process system_ip_blocking_delete form submissions.
1479
 */
1480
function system_ip_blocking_delete_submit($form, &$form_state) {
1481
  $blocked_ip = $form_state['values']['blocked_ip'];
1482
  db_delete('blocked_ips')
1483
    ->condition('iid', $blocked_ip['iid'])
1484
    ->execute();
1485
  watchdog('user', 'Deleted %ip', array('%ip' => $blocked_ip['ip']));
1486
  drupal_set_message(t('The IP address %ip was deleted.', array('%ip' => $blocked_ip['ip'])));
1487
  $form_state['redirect'] = 'admin/config/people/ip-blocking';
1488
}
1489
1490
/**
1491
 * Form builder; The general site information form.
1492
 *
1493
 * @ingroup forms
1494
 * @see system_settings_form()
1495
 */
1496
function system_site_information_settings() {
1497
  $form['site_information'] = array(
1498
    '#type' => 'fieldset',
1499
    '#title' => t('Site details'),
1500
  );
1501
  $form['site_information']['site_name'] = array(
1502
    '#type' => 'textfield',
1503
    '#title' => t('Site name'),
1504
    '#default_value' => variable_get('site_name', 'Drupal'),
1505
    '#required' => TRUE
1506
  );
1507
  $form['site_information']['site_slogan'] = array(
1508
    '#type' => 'textfield',
1509
    '#title' => t('Slogan'),
1510
    '#default_value' => variable_get('site_slogan', ''),
1511
    '#description' => t("How this is used depends on your site's theme."),
1512
  );
1513
  $form['site_information']['site_mail'] = array(
1514
    '#type' => 'textfield',
1515
    '#title' => t('E-mail address'),
1516
    '#default_value' => variable_get('site_mail', ini_get('sendmail_from')),
1517
    '#description' => t("The <em>From</em> address in automated e-mails sent during registration and new password requests, and other notifications. (Use an address ending in your site's domain to help prevent this e-mail being flagged as spam.)"),
1518
    '#required' => TRUE,
1519
  );
1520
  $form['front_page'] = array(
1521
    '#type' => 'fieldset',
1522
    '#title' => t('Front page'),
1523
  );
1524
  $form['front_page']['default_nodes_main'] = array(
1525
    '#type' => 'select', '#title' => t('Number of posts on front page'),
1526
    '#default_value' => variable_get('default_nodes_main', 10),
1527
    '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)),
1528
    '#description' => t('The maximum number of posts displayed on overview pages such as the front page.')
1529
  );
1530
  $form['front_page']['site_frontpage'] = array(
1531
    '#type' => 'textfield',
1532
    '#title' => t('Default front page'),
1533
    '#default_value' => (variable_get('site_frontpage')!='node'?drupal_get_path_alias(variable_get('site_frontpage', 'node')):''),
1534
    '#size' => 40,
1535
    '#description' => t('Optionally, specify a relative URL to display as the front page.  Leave blank to display the default content feed.'),
1536
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
1537
  );
1538
  $form['error_page'] = array(
1539
    '#type' => 'fieldset',
1540
    '#title' => t('Error pages'),
1541
  );
1542
  $form['error_page']['site_403'] = array(
1543
    '#type' => 'textfield',
1544
    '#title' => t('Default 403 (access denied) page'),
1545
    '#default_value' => variable_get('site_403', ''),
1546
    '#size' => 40,
1547
    '#description' => t('This page is displayed when the requested document is denied to the current user. Leave blank to display a generic "access denied" page.'),
1548
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=')
1549
  );
1550
  $form['error_page']['site_404'] = array(
1551
    '#type' => 'textfield',
1552
    '#title' => t('Default 404 (not found) page'),
1553
    '#default_value' => variable_get('site_404', ''),
1554
    '#size' => 40,
1555
    '#description' => t('This page is displayed when no other content matches the requested document. Leave blank to display a generic "page not found" page.'),
1556
    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=')
1557
  );
1558
1559
  $form['#validate'][] = 'system_site_information_settings_validate';
1560
1561
  return system_settings_form($form);
1562
}
1563
1564
/**
1565
 * Validates the submitted site-information form.
1566
 */
1567
function system_site_information_settings_validate($form, &$form_state) {
1568
  // Validate the e-mail address.
1569
  if ($error = user_validate_mail($form_state['values']['site_mail'])) {
1570
    form_set_error('site_mail', $error);
1571
  }
1572
  // Check for empty front page path.
1573
  if (empty($form_state['values']['site_frontpage'])) {
1574
    // Set to default "node".
1575
    form_set_value($form['front_page']['site_frontpage'], 'node', $form_state);
1576
  }
1577
  else {
1578
    // Get the normal path of the front page.
1579
    form_set_value($form['front_page']['site_frontpage'], drupal_get_normal_path($form_state['values']['site_frontpage']), $form_state);
1580
  }
1581
  // Validate front page path.
1582
  if (!drupal_valid_path($form_state['values']['site_frontpage'])) {
1583
    form_set_error('site_frontpage', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_frontpage'])));
1584
  }
1585
  // Get the normal paths of both error pages.
1586
  if (!empty($form_state['values']['site_403'])) {
1587
    form_set_value($form['error_page']['site_403'], drupal_get_normal_path($form_state['values']['site_403']), $form_state);
1588
  }
1589
  if (!empty($form_state['values']['site_404'])) {
1590
    form_set_value($form['error_page']['site_404'], drupal_get_normal_path($form_state['values']['site_404']), $form_state);
1591
  }
1592
  // Validate 403 error path.
1593
  if (!empty($form_state['values']['site_403']) && !drupal_valid_path($form_state['values']['site_403'])) {
1594
    form_set_error('site_403', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_403'])));
1595
  }
1596
  // Validate 404 error path.
1597
  if (!empty($form_state['values']['site_404']) && !drupal_valid_path($form_state['values']['site_404'])) {
1598
    form_set_error('site_404', t("The path '%path' is either invalid or you do not have access to it.", array('%path' => $form_state['values']['site_404'])));
1599
  }
1600
}
1601
1602
/**
1603
 * Form builder; Cron form.
1604
 *
1605
 * @see system_settings_form()
1606
 * @ingroup forms
1607
 */
1608
function system_cron_settings() {
1609
  global $base_url;
1610
  $form['description'] = array(
1611
    '#markup' => '<p>' . t('Cron takes care of running periodic tasks like checking for updates and indexing content for search.') . '</p>',
1612
  );
1613
  $form['run'] = array(
1614
    '#type' => 'submit',
1615
    '#value' => t('Run cron'),
1616
    '#submit' => array('system_run_cron_submit'),
1617
  );
1618
  $status = '<p>' . t('Last run: %cron-last ago.', array('%cron-last' => format_interval(REQUEST_TIME - variable_get('cron_last')),)) . '</p>';
1619
  $form['status'] = array(
1620
    '#markup' => $status,
1621
  );
1622
1623
  $form['cron_url'] = array(
1624
    '#markup' => '<p>' . t('To run cron from outside the site, go to <a href="!cron">!cron</a>', array('!cron' => url($base_url . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => variable_get('cron_key', 'drupal')))))) . '</p>',
1625
  );
1626
1627
  $form['cron'] = array(
1628
    '#type' => 'fieldset',
1629
  );
1630
  $form['cron']['cron_safe_threshold'] = array(
1631
    '#type' => 'select',
1632
    '#title' => t('Run cron every'),
1633
    '#description' => t('More information about setting up scheduled tasks can be found by <a href="@url">reading the cron tutorial on drupal.org</a>.', array('@url' => url('http://drupal.org/cron'))),
1634
    '#default_value' => variable_get('cron_safe_threshold', DRUPAL_CRON_DEFAULT_THRESHOLD),
1635
    '#options' => array(0 => t('Never')) + drupal_map_assoc(array(3600, 10800, 21600, 43200, 86400, 604800), 'format_interval'),
1636
  );
1637
1638
  return system_settings_form($form);
1639
}
1640
1641
/**
1642
 * Submit callback; run cron.
1643
 *
1644
 * @ingroup forms
1645
 */
1646
function system_run_cron_submit($form, &$form_state) {
1647
  // Run cron manually from Cron form.
1648
  if (drupal_cron_run()) {
1649
    drupal_set_message(t('Cron run successfully.'));
1650
  }
1651
  else {
1652
    drupal_set_message(t('Cron run failed.'), 'error');
1653
  }
1654
1655
  drupal_goto('admin/config/system/cron');
1656
}
1657
1658
/**
1659
 * Form builder; Configure error reporting settings.
1660
 *
1661
 * @ingroup forms
1662
 * @see system_settings_form()
1663
 */
1664
function system_logging_settings() {
1665
  $form['error_level'] = array(
1666
    '#type' => 'radios',
1667
    '#title' => t('Error messages to display'),
1668
    '#default_value' => variable_get('error_level', ERROR_REPORTING_DISPLAY_ALL),
1669
    '#options' => array(
1670
      ERROR_REPORTING_HIDE => t('None'),
1671
      ERROR_REPORTING_DISPLAY_SOME => t('Errors and warnings'),
1672
      ERROR_REPORTING_DISPLAY_ALL => t('All messages'),
1673
    ),
1674
    '#description' => t('It is recommended that sites running on production environments do not display any errors.'),
1675
  );
1676
1677
  return system_settings_form($form);
1678
}
1679
1680
/**
1681
 * Form builder; Configure site performance settings.
1682
 *
1683
 * @ingroup forms
1684
 * @see system_settings_form()
1685
 */
1686
function system_performance_settings() {
1687
  drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
1688
1689
  $form['clear_cache'] = array(
1690
    '#type' => 'fieldset',
1691
    '#title' => t('Clear cache'),
1692
  );
1693
1694
  $form['clear_cache']['clear'] = array(
1695
    '#type' => 'submit',
1696
    '#value' => t('Clear all caches'),
1697
    '#submit' => array('system_clear_cache_submit'),
1698
  );
1699
1700
  $form['caching'] = array(
1701
    '#type' => 'fieldset',
1702
    '#title' => t('Caching'),
1703
  );
1704
1705
  $cache = variable_get('cache', 0);
1706
  $form['caching']['cache'] = array(
1707
    '#type' => 'checkbox',
1708
    '#title' => t('Cache pages for anonymous users'),
1709
    '#default_value' => $cache,
1710
    '#weight' => -2,
1711
  );
1712
  $period = drupal_map_assoc(array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400), 'format_interval');
1713
  $period[0] = '<' . t('none') . '>';
1714
  $form['caching']['cache_lifetime'] = array(
1715
    '#type' => 'select',
1716
    '#title' => t('Minimum cache lifetime'),
1717
    '#default_value' => variable_get('cache_lifetime', 0),
1718
    '#options' => $period,
1719
    '#description' => t('Cached pages will not be re-created until at least this much time has elapsed.')
1720
  );
1721
  $form['caching']['page_cache_maximum_age'] = array(
1722
    '#type' => 'select',
1723
    '#title' => t('Expiration of cached pages'),
1724
    '#default_value' => variable_get('page_cache_maximum_age', 0),
1725
    '#options' => $period,
1726
    '#description' => t('The maximum time an external cache can use an old version of a page.')
1727
  );
1728
1729
  $directory = 'public://';
1730
  $is_writable = is_dir($directory) && is_writable($directory);
1731
  $disabled = !$is_writable;
1732
  $disabled_message = '';
1733
  if (!$is_writable) {
1734
    $disabled_message = ' ' . t('<strong class="error">Set up the <a href="!file-system">public files directory</a> to make these optimizations available.</strong>', array('!file-system' => url('admin/config/media/file-system')));
1735
  }
1736
1737
  $form['bandwidth_optimization'] = array(
1738
    '#type' => 'fieldset',
1739
    '#title' => t('Bandwidth optimization'),
1740
    '#description' => t('External resources can be optimized automatically, which can reduce both the size and number of requests made to your website.') . $disabled_message,
1741
  );
1742
1743
  $js_hide = $cache ? '' : ' class="js-hide"';
1744
  $form['bandwidth_optimization']['page_compression'] = array(
1745
    '#type' => 'checkbox',
1746
    '#title' => t('Compress cached pages.'),
1747
    '#default_value' => variable_get('page_compression', TRUE),
1748
    '#prefix' => '<div id="page-compression-wrapper"' . $js_hide . '>',
1749
    '#suffix' => '</div>',
1750
  );
1751
  $form['bandwidth_optimization']['preprocess_css'] = array(
1752
    '#type' => 'checkbox',
1753
    '#title' => t('Aggregate and compress CSS files.'),
1754
    '#default_value' => intval(variable_get('preprocess_css', 0) && $is_writable),
1755
    '#disabled' => $disabled,
1756
  );
1757
  $form['bandwidth_optimization']['preprocess_js'] = array(
1758
    '#type' => 'checkbox',
1759
    '#title' => t('Aggregate JavaScript files.'),
1760
    '#default_value' => intval(variable_get('preprocess_js', 0) && $is_writable),
1761
    '#disabled' => $disabled,
1762
  );
1763
1764
  $form['#submit'][] = 'drupal_clear_css_cache';
1765
  $form['#submit'][] = 'drupal_clear_js_cache';
1766
  // This form allows page compression settings to be changed, which can
1767
  // invalidate the page cache, so it needs to be cleared on form submit.
1768
  $form['#submit'][] = 'system_clear_page_cache_submit';
1769
1770
  return system_settings_form($form);
1771
}
1772
1773
/**
1774
 * Submit callback; clear system caches.
1775
 *
1776
 * @ingroup forms
1777
 */
1778
function system_clear_cache_submit($form, &$form_state) {
1779
  drupal_flush_all_caches();
1780
  drupal_set_message(t('Caches cleared.'));
1781
}
1782
1783
/**
1784
 * Submit callback; clear the page cache.
1785
 *
1786
 * @ingroup forms
1787
 */
1788
function system_clear_page_cache_submit($form, &$form_state) {
1789
  cache_clear_all('*', 'cache_page', TRUE);
1790
}
1791
1792
/**
1793
 * Form builder; Configure the site file handling.
1794
 *
1795
 * @ingroup forms
1796
 * @see system_settings_form()
1797
 */
1798
function system_file_system_settings() {
1799
  $form['file_public_path'] = array(
1800
    '#type' => 'textfield',
1801
    '#title' => t('Public file system path'),
1802
    '#default_value' => variable_get('file_public_path', conf_path() . '/files'),
1803
    '#maxlength' => 255,
1804
    '#description' => t('A local file system path where public files will be stored. This directory must exist and be writable by Drupal. This directory must be relative to the Drupal installation directory and be accessible over the web.'),
1805
    '#after_build' => array('system_check_directory'),
1806
  );
1807
1808
  $form['file_private_path'] = array(
1809
    '#type' => 'textfield',
1810
    '#title' => t('Private file system path'),
1811
    '#default_value' => variable_get('file_private_path', ''),
1812
    '#maxlength' => 255,
1813
    '#description' => t('An existing local file system path for storing private files. It should be writable by Drupal and not accessible over the web. See the online handbook for <a href="@handbook">more information about securing private files</a>.', array('@handbook' => 'http://drupal.org/documentation/modules/file')),
1814
    '#after_build' => array('system_check_directory'),
1815
  );
1816
1817
  $form['file_temporary_path'] = array(
1818
    '#type' => 'textfield',
1819
    '#title' => t('Temporary directory'),
1820
    '#default_value' => variable_get('file_temporary_path', file_directory_temp()),
1821
    '#maxlength' => 255,
1822
    '#description' => t('A local file system path where temporary files will be stored. This directory should not be accessible over the web.'),
1823
    '#after_build' => array('system_check_directory'),
1824
  );
1825
  // Any visible, writeable wrapper can potentially be used for the files
1826
  // directory, including a remote file system that integrates with a CDN.
1827
  foreach (file_get_stream_wrappers(STREAM_WRAPPERS_WRITE_VISIBLE) as $scheme => $info) {
1828
    $options[$scheme] = check_plain($info['description']);
1829
  }
1830
1831
  if (!empty($options)) {
1832
    $form['file_default_scheme'] = array(
1833
      '#type' => 'radios',
1834
      '#title' => t('Default download method'),
1835
      '#default_value' => variable_get('file_default_scheme', isset($options['public']) ? 'public' : key($options)),
1836
      '#options' => $options,
1837
      '#description' => t('This setting is used as the preferred download method. The use of public files is more efficient, but does not provide any access control.'),
1838
    );
1839
  }
1840
1841
  return system_settings_form($form);
1842
}
1843
1844
/**
1845
 * Form builder; Configure site image toolkit usage.
1846
 *
1847
 * @ingroup forms
1848
 * @see system_settings_form()
1849
 */
1850
function system_image_toolkit_settings() {
1851
  $toolkits_available = image_get_available_toolkits();
1852
  $current_toolkit = image_get_toolkit();
1853
1854
  if (count($toolkits_available) == 0) {
1855
    variable_del('image_toolkit');
1856
    $form['image_toolkit_help'] = array(
1857
      '#markup' => t("No image toolkits were detected. Drupal includes support for <a href='!gd-link'>PHP's built-in image processing functions</a> but they were not detected on this system. You should consult your system administrator to have them enabled, or try using a third party toolkit.", array('gd-link' => url('http://php.net/gd'))),
1858
    );
1859
    return $form;
1860
  }
1861
1862
  if (count($toolkits_available) > 1) {
1863
    $form['image_toolkit'] = array(
1864
      '#type' => 'radios',
1865
      '#title' => t('Select an image processing toolkit'),
1866
      '#default_value' => variable_get('image_toolkit', $current_toolkit),
1867
      '#options' => $toolkits_available
1868
    );
1869
  }
1870
  else {
1871
    variable_set('image_toolkit', key($toolkits_available));
1872
  }
1873
1874
  // Get the toolkit's settings form.
1875
  $function = 'image_' . $current_toolkit . '_settings';
1876
  if (function_exists($function)) {
1877
    $form['image_toolkit_settings'] = $function();
1878
  }
1879
1880
  return system_settings_form($form);
1881
}
1882
1883
/**
1884
 * Form builder; Configure how the site handles RSS feeds.
1885
 *
1886
 * @ingroup forms
1887
 * @see system_settings_form()
1888
 */
1889
function system_rss_feeds_settings() {
1890
  $form['feed_description'] = array(
1891
    '#type' => 'textarea',
1892
    '#title' => t('Feed description'),
1893
    '#default_value' => variable_get('feed_description', ''),
1894
    '#description' => t('Description of your site, included in each feed.')
1895
  );
1896
  $form['feed_default_items'] = array(
1897
    '#type' => 'select',
1898
    '#title' => t('Number of items in each feed'),
1899
    '#default_value' => variable_get('feed_default_items', 10),
1900
    '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)),
1901
    '#description' => t('Default number of items to include in each feed.')
1902
  );
1903
  $form['feed_item_length'] = array(
1904
    '#type' => 'select',
1905
    '#title' => t('Feed content'),
1906
    '#default_value' => variable_get('feed_item_length', 'fulltext'),
1907
    '#options' => array('title' => t('Titles only'), 'teaser' => t('Titles plus teaser'), 'fulltext' => t('Full text')),
1908
    '#description' => t('Global setting for the default display of content items in each feed.')
1909
  );
1910
1911
  return system_settings_form($form);
1912
}
1913
1914
/**
1915
 * Form builder; Configure the site regional settings.
1916
 *
1917
 * @ingroup forms
1918
 * @see system_settings_form()
1919
 * @see system_regional_settings_submit()
1920
 */
1921
function system_regional_settings() {
1922
  include_once DRUPAL_ROOT . '/includes/locale.inc';
1923
  $countries = country_get_list();
1924
1925
  // Date settings:
1926
  $zones = system_time_zones();
1927
1928
  $form['locale'] = array(
1929
    '#type' => 'fieldset',
1930
    '#title' => t('Locale'),
1931
  );
1932
1933
  $form['locale']['site_default_country'] = array(
1934
    '#type' => 'select',
1935
    '#title' => t('Default country'),
1936
    '#empty_value' => '',
1937
    '#default_value' => variable_get('site_default_country', ''),
1938
    '#options' => $countries,
1939
    '#attributes' => array('class' => array('country-detect')),
1940
  );
1941
1942
  $form['locale']['date_first_day'] = array(
1943
    '#type' => 'select',
1944
    '#title' => t('First day of week'),
1945
    '#default_value' => variable_get('date_first_day', 0),
1946
    '#options' => array(0 => t('Sunday'), 1 => t('Monday'), 2 => t('Tuesday'), 3 => t('Wednesday'), 4 => t('Thursday'), 5 => t('Friday'), 6 => t('Saturday')),
1947
  );
1948
1949
  $form['timezone'] = array(
1950
    '#type' => 'fieldset',
1951
    '#title' => t('Time zones'),
1952
  );
1953
1954
  $form['timezone']['date_default_timezone'] = array(
1955
    '#type' => 'select',
1956
    '#title' => t('Default time zone'),
1957
    '#default_value' => variable_get('date_default_timezone', date_default_timezone_get()),
1958
    '#options' => $zones,
1959
  );
1960
1961
  $configurable_timezones = variable_get('configurable_timezones', 1);
1962
  $form['timezone']['configurable_timezones'] = array(
1963
    '#type' => 'checkbox',
1964
    '#title' => t('Users may set their own time zone.'),
1965
    '#default_value' => $configurable_timezones,
1966
  );
1967
1968
  $form['timezone']['configurable_timezones_wrapper'] =  array(
1969
    '#type' => 'container',
1970
    '#states' => array(
1971
      // Hide the user configured timezone settings when users are forced to use
1972
      // the default setting.
1973
      'invisible' => array(
1974
        'input[name="configurable_timezones"]' => array('checked' => FALSE),
1975
      ),
1976
    ),
1977
  );
1978
  $form['timezone']['configurable_timezones_wrapper']['empty_timezone_message'] = array(
1979
    '#type' => 'checkbox',
1980
    '#title' => t('Remind users at login if their time zone is not set.'),
1981
    '#default_value' => variable_get('empty_timezone_message', 0),
1982
    '#description' => t('Only applied if users may set their own time zone.')
1983
  );
1984
1985
  $form['timezone']['configurable_timezones_wrapper']['user_default_timezone'] = array(
1986
    '#type' => 'radios',
1987
    '#title' => t('Time zone for new users'),
1988
    '#default_value' => variable_get('user_default_timezone', DRUPAL_USER_TIMEZONE_DEFAULT),
1989
    '#options' => array(
1990
      DRUPAL_USER_TIMEZONE_DEFAULT => t('Default time zone.'),
1991
      DRUPAL_USER_TIMEZONE_EMPTY   => t('Empty time zone.'),
1992
      DRUPAL_USER_TIMEZONE_SELECT  => t('Users may set their own time zone at registration.'),
1993
    ),
1994
    '#description' => t('Only applied if users may set their own time zone.')
1995
  );
1996
1997
  return system_settings_form($form);
1998
}
1999
2000
/**
2001
 * Form builder; Configure the site date and time settings.
2002
 *
2003
 * @ingroup forms
2004
 * @see system_settings_form()
2005
 */
2006
function system_date_time_settings() {
2007
  // Get list of all available date types.
2008
  drupal_static_reset('system_get_date_types');
2009
  $format_types = system_get_date_types();
2010
2011
  // Get list of all available date formats.
2012
  $all_formats = array();
2013
  drupal_static_reset('system_get_date_formats');
2014
  $date_formats = system_get_date_formats(); // Call this to rebuild the list, and to have default list.
2015
  foreach ($date_formats as $type => $format_info) {
2016
    $all_formats = array_merge($all_formats, $format_info);
2017
  }
2018
  $custom_formats = system_get_date_formats('custom');
2019
  if (!empty($format_types)) {
2020
    foreach ($format_types as $type => $type_info) {
2021
      // If a system type, only show the available formats for that type and
2022
      // custom ones.
2023
      if ($type_info['locked'] == 1) {
2024
        $formats = system_get_date_formats($type);
2025
        if (empty($formats)) {
2026
          $formats = $all_formats;
2027
        }
2028
        elseif (!empty($custom_formats)) {
2029
          $formats = array_merge($formats, $custom_formats);
2030
        }
2031
      }
2032
      // If a user configured type, show all available date formats.
2033
      else {
2034
        $formats = $all_formats;
2035
      }
2036
2037
      $choices = array();
2038
      foreach ($formats as $f => $format) {
2039
        $choices[$f] = format_date(REQUEST_TIME, 'custom', $f);
2040
      }
2041
      reset($formats);
2042
      $default = variable_get('date_format_' . $type, key($formats));
2043
2044
      // Get date type info for this date type.
2045
      $type_info = system_get_date_types($type);
2046
      $form['formats']['#theme'] = 'system_date_time_settings';
2047
2048
      // Show date format select list.
2049
      $form['formats']['format']['date_format_' . $type] = array(
2050
        '#type' => 'select',
2051
        '#title' => check_plain($type_info['title']),
2052
        '#attributes' => array('class' => array('date-format')),
2053
        '#default_value' => (isset($choices[$default]) ? $default : 'custom'),
2054
        '#options' => $choices,
2055
      );
2056
2057
      // If this isn't a system provided type, allow the user to remove it from
2058
      // the system.
2059
      if ($type_info['locked'] == 0) {
2060
        $form['formats']['delete']['date_format_' . $type . '_delete'] = array(
2061
          '#type' => 'link',
2062
          '#title' => t('delete'),
2063
          '#href' => 'admin/config/regional/date-time/types/' . $type . '/delete',
2064
        );
2065
      }
2066
    }
2067
  }
2068
2069
  // Display a message if no date types configured.
2070
  $form['#empty_text'] = t('No date types available. <a href="@link">Add date type</a>.', array('@link' => url('admin/config/regional/date-time/types/add')));
2071
2072
  return system_settings_form($form);
2073
}
2074
2075
/**
2076
 * Returns HTML for the date settings form.
2077
 *
2078
 * @param $variables
2079
 *   An associative array containing:
2080
 *   - form: A render element representing the form.
2081
 *
2082
 * @ingroup themeable
2083
 */
2084
function theme_system_date_time_settings($variables) {
2085
  $form = $variables['form'];
2086
  $header = array(
2087
    t('Date type'),
2088
    t('Format'),
2089
    t('Operations'),
2090
  );
2091
2092
  foreach (element_children($form['format']) as $key) {
2093
    $delete_key = $key . '_delete';
2094
    $row = array();
2095
    $row[] = $form['format'][$key]['#title'];
2096
    $form['format'][$key]['#title_display'] = 'invisible';
2097
    $row[] = array('data' => drupal_render($form['format'][$key]));
2098
    $row[] = array('data' => drupal_render($form['delete'][$delete_key]));
2099
    $rows[] = $row;
2100
  }
2101
2102
  $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'system-date-types')));
2103
  $output .= drupal_render_children($form);
2104
2105
  return $output;
2106
}
2107
2108
2109
/**
2110
 * Add new date type.
2111
 *
2112
 * @ingroup forms
2113
 * @ingroup system_add_date_format_type_form_validate()
2114
 * @ingroup system_add_date_format_type_form_submit()
2115
 */
2116
function system_add_date_format_type_form($form, &$form_state) {
2117
  $form['date_type'] = array(
2118
    '#title' => t('Date type'),
2119
    '#type' => 'textfield',
2120
    '#required' => TRUE,
2121
  );
2122
  $form['machine_name'] = array(
2123
    '#type' => 'machine_name',
2124
    '#machine_name' => array(
2125
      'exists' => 'system_get_date_types',
2126
      'source' => array('date_type'),
2127
    ),
2128
  );
2129
2130
  // Get list of all available date formats.
2131
  $formats = array();
2132
  drupal_static_reset('system_get_date_formats');
2133
  $date_formats = system_get_date_formats(); // Call this to rebuild the list, and to have default list.
2134
  foreach ($date_formats as $type => $format_info) {
2135
    $formats = array_merge($formats, $format_info);
2136
  }
2137
  $custom_formats = system_get_date_formats('custom');
2138
  if (!empty($custom_formats)) {
2139
    $formats = array_merge($formats, $custom_formats);
2140
  }
2141
  $choices = array();
2142
  foreach ($formats as $f => $format) {
2143
    $choices[$f] = format_date(REQUEST_TIME, 'custom', $f);
2144
  }
2145
  // Show date format select list.
2146
  $form['date_format'] = array(
2147
    '#type' => 'select',
2148
    '#title' => t('Date format'),
2149
    '#attributes' => array('class' => array('date-format')),
2150
    '#options' => $choices,
2151
    '#required' => TRUE,
2152
  );
2153
2154
  $form['actions'] = array('#type' => 'actions');
2155
  $form['actions']['submit'] = array(
2156
    '#type' => 'submit',
2157
    '#value' => t('Add date type'),
2158
  );
2159
2160
  $form['#validate'][] = 'system_add_date_format_type_form_validate';
2161
  $form['#submit'][] = 'system_add_date_format_type_form_submit';
2162
2163
  return $form;
2164
}
2165
2166
/**
2167
 * Validate system_add_date_format_type form submissions.
2168
 */
2169
function system_add_date_format_type_form_validate($form, &$form_state) {
2170
  if (!empty($form_state['values']['machine_name']) && !empty($form_state['values']['date_type'])) {
2171
    if (!preg_match("/^[a-zA-Z0-9_]+$/", trim($form_state['values']['machine_name']))) {
2172
      form_set_error('machine_name', t('The date type must contain only alphanumeric characters and underscores.'));
2173
    }
2174
    $types = system_get_date_types();
2175
    if (in_array(trim($form_state['values']['machine_name']), array_keys($types))) {
2176
      form_set_error('machine_name', t('This date type already exists. Enter a unique type.'));
2177
    }
2178
  }
2179
}
2180
2181
/**
2182
 * Process system_add_date_format_type form submissions.
2183
 */
2184
function system_add_date_format_type_form_submit($form, &$form_state) {
2185
  $machine_name = trim($form_state['values']['machine_name']);
2186
2187
  $format_type = array();
2188
  $format_type['title'] = trim($form_state['values']['date_type']);
2189
  $format_type['type'] = $machine_name;
2190
  $format_type['locked'] = 0;
2191
  $format_type['is_new'] = 1;
2192
  system_date_format_type_save($format_type);
2193
  variable_set('date_format_' . $machine_name, $form_state['values']['date_format']);
2194
2195
  drupal_set_message(t('New date type added successfully.'));
2196
  $form_state['redirect'] = 'admin/config/regional/date-time';
2197
}
2198
2199
/**
2200
 * Return the date for a given format string via Ajax.
2201
 */
2202
function system_date_time_lookup() {
2203
  $result = format_date(REQUEST_TIME, 'custom', $_GET['format']);
2204
  drupal_json_output($result);
2205
}
2206
2207
/**
2208
 * Form builder; Configure the site's maintenance status.
2209
 *
2210
 * @ingroup forms
2211
 * @see system_settings_form()
2212
 */
2213
function system_site_maintenance_mode() {
2214
  $form['maintenance_mode'] = array(
2215
    '#type' => 'checkbox',
2216
    '#title' => t('Put site into maintenance mode'),
2217
    '#default_value' => variable_get('maintenance_mode', 0),
2218
    '#description' => t('When enabled, only users with the "Use the site in maintenance mode" <a href="@permissions-url">permission</a> are able to access your site to perform maintenance; all other visitors see the maintenance mode message configured below. Authorized users can log in directly via the <a href="@user-login">user login</a> page.', array('@permissions-url' => url('admin/people/permissions'), '@user-login' => url('user'))),
2219
  );
2220
  $form['maintenance_mode_message'] = array(
2221
    '#type' => 'textarea',
2222
    '#title' => t('Maintenance mode message'),
2223
    '#default_value' => variable_get('maintenance_mode_message', t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')))),
2224
    '#description' => t('Message to show visitors when the site is in maintenance mode.')
2225
  );
2226
2227
  return system_settings_form($form);
2228
}
2229
2230
/**
2231
 * Form builder; Configure clean URL settings.
2232
 *
2233
 * @ingroup forms
2234
 * @see system_settings_form()
2235
 */
2236
function system_clean_url_settings($form, &$form_state) {
2237
  $available = FALSE;
2238
  $conflict = FALSE;
2239
2240
  // If the request URI is a clean URL, clean URLs must be available.
2241
  // Otherwise, run a test.
2242
  if (strpos(request_uri(), '?q=') === FALSE && strpos(request_uri(), '&q=') === FALSE) {
2243
    $available = TRUE;
2244
  }
2245
  else {
2246
    $request = drupal_http_request($GLOBALS['base_url'] . '/admin/config/search/clean-urls/check');
2247
    // If the request returns HTTP 200, clean URLs are available.
2248
    if (isset($request->code) && $request->code == 200) {
2249
      $available = TRUE;
2250
      // If the user started the clean URL test, provide explicit feedback.
2251
      if (isset($form_state['input']['clean_url_test_execute'])) {
2252
        drupal_set_message(t('The clean URL test passed.'));
2253
      }
2254
    }
2255
    else {
2256
      // If the test failed while clean URLs are enabled, make sure clean URLs
2257
      // can be disabled.
2258
      if (variable_get('clean_url', 0)) {
2259
        $conflict = TRUE;
2260
        // Warn the user of a conflicting situation, unless after processing
2261
        // a submitted form.
2262
        if (!isset($form_state['input']['op'])) {
2263
          drupal_set_message(t('Clean URLs are enabled, but the clean URL test failed. Uncheck the box below to disable clean URLs.'), 'warning');
2264
        }
2265
      }
2266
      // If the user started the clean URL test, provide explicit feedback.
2267
      elseif (isset($form_state['input']['clean_url_test_execute'])) {
2268
        drupal_set_message(t('The clean URL test failed.'), 'warning');
2269
      }
2270
    }
2271
  }
2272
2273
  // Show the enable/disable form if clean URLs are available or if the user
2274
  // must be able to resolve a conflicting setting.
2275
  if ($available || $conflict) {
2276
    $form['clean_url'] = array(
2277
      '#type' => 'checkbox',
2278
      '#title' => t('Enable clean URLs'),
2279
      '#default_value' => variable_get('clean_url', 0),
2280
      '#description' => t('Use URLs like <code>example.com/user</code> instead of <code>example.com/?q=user</code>.'),
2281
    );
2282
    $form = system_settings_form($form);
2283
    if ($conflict) {
2284
      // $form_state['redirect'] needs to be set to the non-clean URL,
2285
      // otherwise the setting is not saved.
2286
      $form_state['redirect'] = url('', array('query' => array('q' => '/admin/config/search/clean-urls')));
2287
    }
2288
  }
2289
  // Show the clean URLs test form.
2290
  else {
2291
    drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
2292
2293
    $form_state['redirect'] = url('admin/config/search/clean-urls');
2294
    $form['clean_url_description'] = array(
2295
      '#type' => 'markup',
2296
      '#markup' => '<p>' . t('Use URLs like <code>example.com/user</code> instead of <code>example.com/?q=user</code>.'),
2297
    );
2298
    // Explain why the user is seeing this page and what to expect after
2299
    // clicking the 'Run the clean URL test' button.
2300
    $form['clean_url_test_result'] = array(
2301
      '#type' => 'markup',
2302
      '#markup' => '<p>' . t('Clean URLs cannot be enabled. If you are directed to this page or to a <em>Page not found (404)</em> error after testing for clean URLs, see the <a href="@handbook">online handbook</a>.', array('@handbook' => 'http://drupal.org/node/15365')) . '</p>',
2303
    );
2304
    $form['actions'] = array(
2305
      '#type' => 'actions',
2306
      'clean_url_test' => array(
2307
        '#type' => 'submit',
2308
        '#value' => t('Run the clean URL test'),
2309
      ),
2310
    );
2311
    $form['clean_url_test_execute'] = array(
2312
      '#type' => 'hidden',
2313
      '#value' => 1,
2314
    );
2315
  }
2316
2317
  return $form;
2318
}
2319
2320
/**
2321
 * Menu callback: displays the site status report. Can also be used as a pure check.
2322
 *
2323
 * @param $check
2324
 *   If true, only returns a boolean whether there are system status errors.
2325
 */
2326
function system_status($check = FALSE) {
2327
  // Load .install files
2328
  include_once DRUPAL_ROOT . '/includes/install.inc';
2329
  drupal_load_updates();
2330
2331
  // Check run-time requirements and status information.
2332
  $requirements = module_invoke_all('requirements', 'runtime');
2333
  usort($requirements, '_system_sort_requirements');
2334
2335
  if ($check) {
2336
    return drupal_requirements_severity($requirements) == REQUIREMENT_ERROR;
2337
  }
2338
  // MySQL import might have set the uid of the anonymous user to autoincrement
2339
  // value. Let's try fixing it. See http://drupal.org/node/204411
2340
  db_update('users')
2341
    ->expression('uid', 'uid - uid')
2342
    ->condition('name', '')
2343
    ->condition('pass', '')
2344
    ->condition('status', 0)
2345
    ->execute();
2346
  return theme('status_report', array('requirements' => $requirements));
2347
}
2348
2349
/**
2350
 * Menu callback: run cron manually.
2351
 */
2352
function system_run_cron() {
2353
  // Run cron manually
2354
  if (drupal_cron_run()) {
2355
    drupal_set_message(t('Cron ran successfully.'));
2356
  }
2357
  else {
2358
    drupal_set_message(t('Cron run failed.'), 'error');
2359
  }
2360
2361
  drupal_goto('admin/reports/status');
2362
}
2363
2364
/**
2365
 * Menu callback: return information about PHP.
2366
 */
2367
function system_php() {
2368
  phpinfo();
2369
  drupal_exit();
2370
}
2371
2372
/**
2373
 * Default page callback for batches.
2374
 */
2375
function system_batch_page() {
2376
  require_once DRUPAL_ROOT . '/includes/batch.inc';
2377
  $output = _batch_page();
2378
2379
  if ($output === FALSE) {
2380
    drupal_access_denied();
2381
  }
2382
  elseif (isset($output)) {
2383
    // Force a page without blocks or messages to
2384
    // display a list of collected messages later.
2385
    drupal_set_page_content($output);
2386
    $page = element_info('page');
2387
    $page['#show_messages'] = FALSE;
2388
    return $page;
2389
  }
2390
}
2391
2392
/**
2393
 * Returns HTML for an administrative block for display.
2394
 *
2395
 * @param $variables
2396
 *   An associative array containing:
2397
 *   - block: An array containing information about the block:
2398
 *     - show: A Boolean whether to output the block. Defaults to FALSE.
2399
 *     - title: The block's title.
2400
 *     - content: (optional) Formatted content for the block.
2401
 *     - description: (optional) Description of the block. Only output if
2402
 *       'content' is not set.
2403
 *
2404
 * @ingroup themeable
2405
 */
2406
function theme_admin_block($variables) {
2407
  $block = $variables['block'];
2408
  $output = '';
2409
2410
  // Don't display the block if it has no content to display.
2411
  if (empty($block['show'])) {
2412
    return $output;
2413
  }
2414
2415
  $output .= '<div class="admin-panel">';
2416
  if (!empty($block['title'])) {
2417
    $output .= '<h3>' . $block['title'] . '</h3>';
2418
  }
2419
  if (!empty($block['content'])) {
2420
    $output .= '<div class="body">' . $block['content'] . '</div>';
2421
  }
2422
  else {
2423
    $output .= '<div class="description">' . $block['description'] . '</div>';
2424
  }
2425
  $output .= '</div>';
2426
2427
  return $output;
2428
}
2429
2430
/**
2431
 * Returns HTML for the content of an administrative block.
2432
 *
2433
 * @param $variables
2434
 *   An associative array containing:
2435
 *   - content: An array containing information about the block. Each element
2436
 *     of the array represents an administrative menu item, and must at least
2437
 *     contain the keys 'title', 'href', and 'localized_options', which are
2438
 *     passed to l(). A 'description' key may also be provided.
2439
 *
2440
 * @ingroup themeable
2441
 */
2442
function theme_admin_block_content($variables) {
2443
  $content = $variables['content'];
2444
  $output = '';
2445
2446
  if (!empty($content)) {
2447
    $class = 'admin-list';
2448
    if ($compact = system_admin_compact_mode()) {
2449
      $class .= ' compact';
2450
    }
2451
    $output .= '<dl class="' . $class . '">';
2452
    foreach ($content as $item) {
2453
      $output .= '<dt>' . l($item['title'], $item['href'], $item['localized_options']) . '</dt>';
2454
      if (!$compact && isset($item['description'])) {
2455
        $output .= '<dd>' . filter_xss_admin($item['description']) . '</dd>';
2456
      }
2457
    }
2458
    $output .= '</dl>';
2459
  }
2460
  return $output;
2461
}
2462
2463
/**
2464
 * Returns HTML for an administrative page.
2465
 *
2466
 * @param $variables
2467
 *   An associative array containing:
2468
 *   - blocks: An array of blocks to display. Each array should include a
2469
 *     'title', a 'description', a formatted 'content' and a 'position' which
2470
 *     will control which container it will be in. This is usually 'left' or
2471
 *     'right'.
2472
 *
2473
 * @ingroup themeable
2474
 */
2475
function theme_admin_page($variables) {
2476
  $blocks = $variables['blocks'];
2477
2478
  $stripe = 0;
2479
  $container = array();
2480
2481
  foreach ($blocks as $block) {
2482
    if ($block_output = theme('admin_block', array('block' => $block))) {
2483
      if (empty($block['position'])) {
2484
        // perform automatic striping.
2485
        $block['position'] = ++$stripe % 2 ? 'left' : 'right';
2486
      }
2487
      if (!isset($container[$block['position']])) {
2488
        $container[$block['position']] = '';
2489
      }
2490
      $container[$block['position']] .= $block_output;
2491
    }
2492
  }
2493
2494
  $output = '<div class="admin clearfix">';
2495
  $output .= theme('system_compact_link');
2496
2497
  foreach ($container as $id => $data) {
2498
    $output .= '<div class="' . $id . ' clearfix">';
2499
    $output .= $data;
2500
    $output .= '</div>';
2501
  }
2502
  $output .= '</div>';
2503
  return $output;
2504
}
2505
2506
/**
2507
 * Returns HTML for the output of the dashboard page.
2508
 *
2509
 * @param $variables
2510
 *   An associative array containing:
2511
 *   - menu_items: An array of modules to be displayed.
2512
 *
2513
 * @ingroup themeable
2514
 */
2515
function theme_system_admin_index($variables) {
2516
  $menu_items = $variables['menu_items'];
2517
2518
  $stripe = 0;
2519
  $container = array('left' => '', 'right' => '');
2520
  $flip = array('left' => 'right', 'right' => 'left');
2521
  $position = 'left';
2522
2523
  // Iterate over all modules.
2524
  foreach ($menu_items as $module => $block) {
2525
    list($description, $items) = $block;
2526
2527
    // Output links.
2528
    if (count($items)) {
2529
      $block = array();
2530
      $block['title'] = $module;
2531
      $block['content'] = theme('admin_block_content', array('content' => $items));
2532
      $block['description'] = t($description);
2533
      $block['show'] = TRUE;
2534
2535
      if ($block_output = theme('admin_block', array('block' => $block))) {
2536
        if (!isset($block['position'])) {
2537
          // Perform automatic striping.
2538
          $block['position'] = $position;
2539
          $position = $flip[$position];
2540
        }
2541
        $container[$block['position']] .= $block_output;
2542
      }
2543
    }
2544
  }
2545
2546
  $output = '<div class="admin clearfix">';
2547
  $output .= theme('system_compact_link');
2548
  foreach ($container as $id => $data) {
2549
    $output .= '<div class="' . $id . ' clearfix">';
2550
    $output .= $data;
2551
    $output .= '</div>';
2552
  }
2553
  $output .= '</div>';
2554
2555
  return $output;
2556
}
2557
2558
/**
2559
 * Returns HTML for the status report.
2560
 *
2561
 * @param $variables
2562
 *   An associative array containing:
2563
 *   - requirements: An array of requirements.
2564
 *
2565
 * @ingroup themeable
2566
 */
2567
function theme_status_report($variables) {
2568
  $requirements = $variables['requirements'];
2569
  $severities = array(
2570
    REQUIREMENT_INFO => array(
2571
      'title' => t('Info'),
2572
      'class' => 'info',
2573
    ),
2574
    REQUIREMENT_OK => array(
2575
      'title' => t('OK'),
2576
      'class' => 'ok',
2577
    ),
2578
    REQUIREMENT_WARNING => array(
2579
      'title' => t('Warning'),
2580
      'class' => 'warning',
2581
    ),
2582
    REQUIREMENT_ERROR => array(
2583
      'title' => t('Error'),
2584
      'class' => 'error',
2585
    ),
2586
  );
2587
  $output = '<table class="system-status-report">';
2588
2589
  foreach ($requirements as $requirement) {
2590
    if (empty($requirement['#type'])) {
2591
      $severity = $severities[isset($requirement['severity']) ? (int) $requirement['severity'] : REQUIREMENT_OK];
2592
      $severity['icon'] = '<div title="' . $severity['title'] . '"><span class="element-invisible">' . $severity['title'] . '</span></div>';
2593
2594
      // Output table row(s)
2595
      if (!empty($requirement['description'])) {
2596
        $output .= '<tr class="' . $severity['class'] . ' merge-down"><td class="status-icon">' . $severity['icon'] . '</td><td class="status-title">' . $requirement['title'] . '</td><td class="status-value">' . $requirement['value'] . '</td></tr>';
2597
        $output .= '<tr class="' . $severity['class'] . ' merge-up"><td colspan="3" class="status-description">' . $requirement['description'] . '</td></tr>';
2598
      }
2599
      else {
2600
        $output .= '<tr class="' . $severity['class'] . '"><td class="status-icon">' . $severity['icon'] . '</td><td class="status-title">' . $requirement['title'] . '</td><td class="status-value">' . $requirement['value'] . '</td></tr>';
2601
      }
2602
    }
2603
  }
2604
2605
  $output .= '</table>';
2606
  return $output;
2607
}
2608
2609
/**
2610
 * Returns HTML for the modules form.
2611
 *
2612
 * @param $variables
2613
 *   An associative array containing:
2614
 *   - form: A render element representing the form.
2615
 *
2616
 * @ingroup themeable
2617
 */
2618
function theme_system_modules_fieldset($variables) {
2619
  $form = $variables['form'];
2620
2621
  // Individual table headers.
2622
  $rows = array();
2623
  // Iterate through all the modules, which are
2624
  // children of this fieldset.
2625
  foreach (element_children($form) as $key) {
2626
    // Stick it into $module for easier accessing.
2627
    $module = $form[$key];
2628
    $row = array();
2629
    unset($module['enable']['#title']);
2630
    $row[] = array('class' => array('checkbox'), 'data' => drupal_render($module['enable']));
2631
    $label = '<label';
2632
    if (isset($module['enable']['#id'])) {
2633
      $label .= ' for="' . $module['enable']['#id'] . '"';
2634
    }
2635
    $row[] = $label . '><strong>' . drupal_render($module['name']) . '</strong></label>';
2636
    $row[] = drupal_render($module['version']);
2637
    // Add the description, along with any modules it requires.
2638
    $description = drupal_render($module['description']);
2639
    if ($module['#requires']) {
2640
      $description .= '<div class="admin-requirements">' . t('Requires: !module-list', array('!module-list' => implode(', ', $module['#requires']))) . '</div>';
2641
    }
2642
    if ($module['#required_by']) {
2643
      $description .= '<div class="admin-requirements">' . t('Required by: !module-list', array('!module-list' => implode(', ', $module['#required_by']))) . '</div>';
2644
    }
2645
    $row[] = array('data' => $description, 'class' => array('description'));
2646
    // Display links (such as help or permissions) in their own columns.
2647
    foreach (array('help', 'permissions', 'configure') as $key) {
2648
      $row[] = array('data' => drupal_render($module['links'][$key]), 'class' => array($key));
2649
    }
2650
    $rows[] = $row;
2651
  }
2652
2653
  return theme('table', array('header' => $form['#header'], 'rows' => $rows));
2654
}
2655
2656
/**
2657
 * Returns HTML for a message about incompatible modules.
2658
 *
2659
 * @param $variables
2660
 *   An associative array containing:
2661
 *   - message: The form array representing the currently disabled modules.
2662
 *
2663
 * @ingroup themeable
2664
 */
2665
function theme_system_modules_incompatible($variables) {
2666
  return '<div class="incompatible">' . $variables['message'] . '</div>';
2667
}
2668
2669
/**
2670
 * Returns HTML for a table of currently disabled modules.
2671
 *
2672
 * @param $variables
2673
 *   An associative array containing:
2674
 *   - form: A render element representing the form.
2675
 *
2676
 * @ingroup themeable
2677
 */
2678
function theme_system_modules_uninstall($variables) {
2679
  $form = $variables['form'];
2680
2681
  // No theming for the confirm form.
2682
  if (isset($form['confirm'])) {
2683
    return drupal_render($form);
2684
  }
2685
2686
  // Table headers.
2687
  $header = array(t('Uninstall'),
2688
    t('Name'),
2689
    t('Description'),
2690
  );
2691
2692
  // Display table.
2693
  $rows = array();
2694
  foreach (element_children($form['modules']) as $module) {
2695
    if (!empty($form['modules'][$module]['#required_by'])) {
2696
      $disabled_message = format_plural(count($form['modules'][$module]['#required_by']),
2697
        'To uninstall @module, the following module must be uninstalled first: @required_modules',
2698
        'To uninstall @module, the following modules must be uninstalled first: @required_modules',
2699
        array('@module' => $form['modules'][$module]['#module_name'], '@required_modules' => implode(', ', $form['modules'][$module]['#required_by'])));
2700
      $disabled_message = '<div class="admin-requirements">' . $disabled_message . '</div>';
2701
    }
2702
    else {
2703
      $disabled_message = '';
2704
    }
2705
    $rows[] = array(
2706
      array('data' => drupal_render($form['uninstall'][$module]), 'align' => 'center'),
2707
      '<strong><label for="' . $form['uninstall'][$module]['#id'] . '">' . drupal_render($form['modules'][$module]['name']) . '</label></strong>',
2708
      array('data' => drupal_render($form['modules'][$module]['description']) . $disabled_message, 'class' => array('description')),
2709
    );
2710
  }
2711
2712
  $output  = theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No modules are available to uninstall.')));
2713
  $output .= drupal_render_children($form);
2714
2715
  return $output;
2716
}
2717
2718
/**
2719
 * Returns HTML for the Appearance page.
2720
 *
2721
 * @param $variables
2722
 *   An associative array containing:
2723
 *   - theme_groups: An associative array containing groups of themes.
2724
 *
2725
 * @ingroup themeable
2726
 */
2727
function theme_system_themes_page($variables) {
2728
  $theme_groups = $variables['theme_groups'];
2729
2730
  $output = '<div id="system-themes-page">';
2731
2732
  foreach ($variables['theme_group_titles'] as $state => $title) {
2733
    if (!count($theme_groups[$state])) {
2734
      // Skip this group of themes if no theme is there.
2735
      continue;
2736
    }
2737
    // Start new theme group.
2738
    $output .= '<div class="system-themes-list system-themes-list-'. $state .' clearfix"><h2>'. $title .'</h2>';
2739
2740
    foreach ($theme_groups[$state] as $theme) {
2741
2742
      // Theme the screenshot.
2743
      $screenshot = $theme->screenshot ? theme('image', $theme->screenshot) : '<div class="no-screenshot">' . t('no screenshot') . '</div>';
2744
2745
      // Localize the theme description.
2746
      $description = t($theme->info['description']);
2747
2748
      // Style theme info
2749
      $notes = count($theme->notes) ? ' (' . join(', ', $theme->notes) . ')' : '';
2750
      $theme->classes[] = 'theme-selector';
2751
      $theme->classes[] = 'clearfix';
2752
      $output .= '<div class="'. join(' ', $theme->classes) .'">' . $screenshot . '<div class="theme-info"><h3>' . $theme->info['name'] . ' ' . (isset($theme->info['version']) ? $theme->info['version'] : '') . $notes . '</h3><div class="theme-description">' . $description . '</div>';
2753
2754
      // Make sure to provide feedback on compatibility.
2755
      if (!empty($theme->incompatible_core)) {
2756
        $output .= '<div class="incompatible">' . t('This version is not compatible with Drupal !core_version and should be replaced.', array('!core_version' => DRUPAL_CORE_COMPATIBILITY)) . '</div>';
2757
      }
2758
      elseif (!empty($theme->incompatible_php)) {
2759
        if (substr_count($theme->info['php'], '.') < 2) {
2760
          $theme->info['php'] .= '.*';
2761
        }
2762
        $output .= '<div class="incompatible">' . t('This theme requires PHP version @php_required and is incompatible with PHP version !php_version.', array('@php_required' => $theme->info['php'], '!php_version' => phpversion())) . '</div>';
2763
      }
2764
      else {
2765
        $output .= theme('links', array('links' => $theme->operations, 'attributes' => array('class' => array('operations', 'clearfix'))));
2766
      }
2767
      $output .= '</div></div>';
2768
    }
2769
    $output .= '</div>';
2770
  }
2771
  $output .= '</div>';
2772
2773
  return $output;
2774
}
2775
2776
/**
2777
 * Menu callback; present a form for deleting a date format.
2778
 */
2779
function system_date_delete_format_form($form, &$form_state, $dfid) {
2780
  $form['dfid'] = array(
2781
    '#type' => 'value',
2782
    '#value' => $dfid,
2783
  );
2784
  $format = system_get_date_format($dfid);
2785
2786
  $output = confirm_form($form,
2787
    t('Are you sure you want to remove the format %format?', array('%format' => format_date(REQUEST_TIME, 'custom', $format->format))),
2788
    'admin/config/regional/date-time/formats',
2789
    t('This action cannot be undone.'),
2790
    t('Remove'), t('Cancel'),
2791
    'confirm'
2792
  );
2793
2794
  return $output;
2795
}
2796
2797
/**
2798
 * Delete a configured date format.
2799
 */
2800
function system_date_delete_format_form_submit($form, &$form_state) {
2801
  if ($form_state['values']['confirm']) {
2802
    $format = system_get_date_format($form_state['values']['dfid']);
2803
    system_date_format_delete($form_state['values']['dfid']);
2804
    drupal_set_message(t('Removed date format %format.', array('%format' => format_date(REQUEST_TIME, 'custom', $format->format))));
2805
    $form_state['redirect'] = 'admin/config/regional/date-time/formats';
2806
  }
2807
}
2808
2809
/**
2810
 * Menu callback; present a form for deleting a date type.
2811
 */
2812
function system_delete_date_format_type_form($form, &$form_state, $format_type) {
2813
  $form['format_type'] = array(
2814
    '#type' => 'value',
2815
    '#value' => $format_type,
2816
  );
2817
  $type_info = system_get_date_types($format_type);
2818
2819
  $output = confirm_form($form,
2820
    t('Are you sure you want to remove the date type %type?', array('%type' => $type_info['title'])),
2821
    'admin/config/regional/date-time',
2822
    t('This action cannot be undone.'),
2823
    t('Remove'), t('Cancel'),
2824
    'confirm'
2825
  );
2826
2827
  return $output;
2828
}
2829
2830
/**
2831
 * Delete a configured date type.
2832
 */
2833
function system_delete_date_format_type_form_submit($form, &$form_state) {
2834
  if ($form_state['values']['confirm']) {
2835
    $type_info = system_get_date_types($form_state['values']['format_type']);
2836
    system_date_format_type_delete($form_state['values']['format_type']);
2837
    drupal_set_message(t('Removed date type %type.', array('%type' => $type_info['title'])));
2838
    $form_state['redirect'] = 'admin/config/regional/date-time';
2839
  }
2840
}
2841
2842
2843
/**
2844
 * Displays the date format strings overview page.
2845
 */
2846
function system_date_time_formats() {
2847
  $header = array(t('Format'), array('data' => t('Operations'), 'colspan' => '2'));
2848
  $rows = array();
2849
2850
  drupal_static_reset('system_get_date_formats');
2851
  $formats = system_get_date_formats('custom');
2852
  if (!empty($formats)) {
2853
    foreach ($formats as $format) {
2854
      $row = array();
2855
      $row[] = array('data' => format_date(REQUEST_TIME, 'custom', $format['format']));
2856
      $row[] = array('data' => l(t('edit'), 'admin/config/regional/date-time/formats/' . $format['dfid'] . '/edit'));
2857
      $row[] = array('data' => l(t('delete'), 'admin/config/regional/date-time/formats/' . $format['dfid'] . '/delete'));
2858
      $rows[] = $row;
2859
    }
2860
  }
2861
2862
  $build['date_formats_table'] = array(
2863
    '#theme' => 'table',
2864
    '#header' => $header,
2865
    '#rows' => $rows,
2866
    '#empty' => t('No custom date formats available. <a href="@link">Add date format</a>.', array('@link' => url('admin/config/regional/date-time/formats/add'))),
2867
  );
2868
2869
  return $build;
2870
}
2871
2872
/**
2873
 * Allow users to add additional date formats.
2874
 */
2875
function system_configure_date_formats_form($form, &$form_state, $dfid = 0) {
2876
  $js_settings = array(
2877
    'type' => 'setting',
2878
    'data' => array(
2879
      'dateTime' => array(
2880
        'date-format' => array(
2881
          'text' => t('Displayed as'),
2882
          'lookup' => url('admin/config/regional/date-time/formats/lookup'),
2883
        ),
2884
      ),
2885
    ),
2886
  );
2887
2888
  if ($dfid) {
2889
    $form['dfid'] = array(
2890
      '#type' => 'value',
2891
      '#value' => $dfid,
2892
    );
2893
    $format = system_get_date_format($dfid);
2894
  }
2895
2896
  $now = ($dfid ? t('Displayed as %date', array('%date' => format_date(REQUEST_TIME, 'custom', $format->format))) : '');
2897
2898
  $form['date_format'] = array(
2899
    '#type' => 'textfield',
2900
    '#title' => t('Format string'),
2901
    '#maxlength' => 100,
2902
    '#description' => t('A user-defined date format. See the <a href="@url">PHP manual</a> for available options.', array('@url' => 'http://php.net/manual/function.date.php')),
2903
    '#default_value' => ($dfid ? $format->format : ''),
2904
    '#field_suffix' => ' <small id="edit-date-format-suffix">' . $now . '</small>',
2905
    '#attached' => array(
2906
      'js' => array(drupal_get_path('module', 'system') . '/system.js', $js_settings),
2907
    ),
2908
    '#required' => TRUE,
2909
  );
2910
2911
  $form['actions'] = array('#type' => 'actions');
2912
  $form['actions']['update'] = array(
2913
    '#type' => 'submit',
2914
    '#value' => ($dfid ? t('Save format') : t('Add format')),
2915
  );
2916
2917
  $form['#validate'][] = 'system_add_date_formats_form_validate';
2918
  $form['#submit'][] = 'system_add_date_formats_form_submit';
2919
2920
  return $form;
2921
}
2922
2923
/**
2924
 * Validate new date format string submission.
2925
 */
2926
function system_add_date_formats_form_validate($form, &$form_state) {
2927
  $formats = system_get_date_formats('custom');
2928
  $format = trim($form_state['values']['date_format']);
2929
  if (!empty($formats) && in_array($format, array_keys($formats)) && (!isset($form_state['values']['dfid']) || $form_state['values']['dfid'] != $formats[$format]['dfid'])) {
2930
    form_set_error('date_format', t('This format already exists. Enter a unique format string.'));
2931
  }
2932
}
2933
2934
/**
2935
 * Process new date format string submission.
2936
 */
2937
function system_add_date_formats_form_submit($form, &$form_state) {
2938
  $format = array();
2939
  $format['format'] = trim($form_state['values']['date_format']);
2940
  $format['type'] = 'custom';
2941
  $format['locked'] = 0;
2942
  if (!empty($form_state['values']['dfid'])) {
2943
    system_date_format_save($format, $form_state['values']['dfid']);
2944
    drupal_set_message(t('Custom date format updated.'));
2945
  }
2946
  else {
2947
    $format['is_new'] = 1;
2948
    system_date_format_save($format);
2949
    drupal_set_message(t('Custom date format added.'));
2950
  }
2951
2952
  $form_state['redirect'] = 'admin/config/regional/date-time/formats';
2953
}
2954
2955
/**
2956
 * Menu callback; Displays an overview of available and configured actions.
2957
 */
2958
function system_actions_manage() {
2959
  actions_synchronize();
2960
  $actions = actions_list();
2961
  $actions_map = actions_actions_map($actions);
2962
  $options = array();
2963
  $unconfigurable = array();
2964
2965
  foreach ($actions_map as $key => $array) {
2966
    if ($array['configurable']) {
2967
      $options[$key] = $array['label'] . '...';
2968
    }
2969
    else {
2970
      $unconfigurable[] = $array;
2971
    }
2972
  }
2973
2974
  $row = array();
2975
  $instances_present = db_query("SELECT aid FROM {actions} WHERE parameters <> ''")->fetchField();
2976
  $header = array(
2977
    array('data' => t('Action type'), 'field' => 'type'),
2978
    array('data' => t('Label'), 'field' => 'label'),
2979
    array('data' => $instances_present ? t('Operations') : '', 'colspan' => '2')
2980
  );
2981
  $query = db_select('actions')->extend('PagerDefault')->extend('TableSort');
2982
  $result = $query
2983
    ->fields('actions')
2984
    ->limit(50)
2985
    ->orderByHeader($header)
2986
    ->execute();
2987
2988
  foreach ($result as $action) {
2989
    $row[] = array(
2990
      array('data' => $action->type),
2991
      array('data' => check_plain($action->label)),
2992
      array('data' => $action->parameters ? l(t('configure'), "admin/config/system/actions/configure/$action->aid") : ''),
2993
      array('data' => $action->parameters ? l(t('delete'), "admin/config/system/actions/delete/$action->aid") : '')
2994
    );
2995
  }
2996
2997
  if ($row) {
2998
    $pager = theme('pager');
2999
    if (!empty($pager)) {
3000
      $row[] = array(array('data' => $pager, 'colspan' => '3'));
3001
    }
3002
    $build['system_actions_header'] = array('#markup' => '<h3>' . t('Available actions:') . '</h3>');
3003
    $build['system_actions_table'] = array('#markup' => theme('table', array('header' => $header, 'rows' => $row)));
3004
  }
3005
3006
  if ($actions_map) {
3007
    $build['system_actions_manage_form'] = drupal_get_form('system_actions_manage_form', $options);
3008
  }
3009
3010
  return $build;
3011
}
3012
3013
/**
3014
 * Define the form for the actions overview page.
3015
 *
3016
 * @param $form_state
3017
 *   An associative array containing the current state of the form; not used.
3018
 * @param $options
3019
 *   An array of configurable actions.
3020
 * @return
3021
 *   Form definition.
3022
 *
3023
 * @ingroup forms
3024
 * @see system_actions_manage_form_submit()
3025
 */
3026
function system_actions_manage_form($form, &$form_state, $options = array()) {
3027
  $form['parent'] = array(
3028
    '#type' => 'fieldset',
3029
    '#title' => t('Create an advanced action'),
3030
    '#attributes' => array('class' => array('container-inline')),
3031
  );
3032
  $form['parent']['action'] = array(
3033
    '#type' => 'select',
3034
    '#title' => t('Action'),
3035
    '#title_display' => 'invisible',
3036
    '#options' => $options,
3037
    '#empty_option' => t('Choose an advanced action'),
3038
  );
3039
  $form['parent']['actions'] = array('#type' => 'actions');
3040
  $form['parent']['actions']['submit'] = array(
3041
    '#type' => 'submit',
3042
    '#value' => t('Create'),
3043
  );
3044
  return $form;
3045
}
3046
3047
/**
3048
 * Process system_actions_manage form submissions.
3049
 *
3050
 * @see system_actions_manage_form()
3051
 */
3052
function system_actions_manage_form_submit($form, &$form_state) {
3053
  if ($form_state['values']['action']) {
3054
    $form_state['redirect'] = 'admin/config/system/actions/configure/' . $form_state['values']['action'];
3055
  }
3056
}
3057
3058
/**
3059
 * Menu callback; Creates the form for configuration of a single action.
3060
 *
3061
 * We provide the "Description" field. The rest of the form is provided by the
3062
 * action. We then provide the Save button. Because we are combining unknown
3063
 * form elements with the action configuration form, we use an 'actions_' prefix
3064
 * on our elements.
3065
 *
3066
 * @param $action
3067
 *   Hash of an action ID or an integer. If it is a hash, we are
3068
 *   creating a new instance. If it is an integer, we are editing an existing
3069
 *   instance.
3070
 * @return
3071
 *   A form definition.
3072
 *
3073
 * @see system_actions_configure_validate()
3074
 * @see system_actions_configure_submit()
3075
 */
3076
function system_actions_configure($form, &$form_state, $action = NULL) {
3077
  if ($action === NULL) {
3078
    drupal_goto('admin/config/system/actions');
3079
  }
3080
3081
  $actions_map = actions_actions_map(actions_list());
3082
  $edit = array();
3083
3084
  // Numeric action denotes saved instance of a configurable action.
3085
  if (is_numeric($action)) {
3086
    $aid = $action;
3087
    // Load stored parameter values from database.
3088
    $data = db_query("SELECT * FROM {actions} WHERE aid = :aid", array(':aid' => $aid))->fetch();
3089
    $edit['actions_label'] = $data->label;
3090
    $edit['actions_type'] = $data->type;
3091
    $function = $data->callback;
3092
    $action = drupal_hash_base64($data->callback);
3093
    $params = unserialize($data->parameters);
3094
    if ($params) {
3095
      foreach ($params as $name => $val) {
3096
        $edit[$name] = $val;
3097
      }
3098
    }
3099
  }
3100
  // Otherwise, we are creating a new action instance.
3101
  else {
3102
    $function = $actions_map[$action]['callback'];
3103
    $edit['actions_label'] = $actions_map[$action]['label'];
3104
    $edit['actions_type'] = $actions_map[$action]['type'];
3105
  }
3106
3107
  $form['actions_label'] = array(
3108
    '#type' => 'textfield',
3109
    '#title' => t('Label'),
3110
    '#default_value' => $edit['actions_label'],
3111
    '#maxlength' => '255',
3112
    '#description' => t('A unique label for this advanced action. This label will be displayed in the interface of modules that integrate with actions, such as Trigger module.'),
3113
    '#weight' => -10
3114
  );
3115
  $action_form = $function . '_form';
3116
  $form = array_merge($form, $action_form($edit));
3117
  $form['actions_type'] = array(
3118
    '#type' => 'value',
3119
    '#value' => $edit['actions_type'],
3120
  );
3121
  $form['actions_action'] = array(
3122
    '#type' => 'hidden',
3123
    '#value' => $action,
3124
  );
3125
  // $aid is set when configuring an existing action instance.
3126
  if (isset($aid)) {
3127
    $form['actions_aid'] = array(
3128
      '#type' => 'hidden',
3129
      '#value' => $aid,
3130
    );
3131
  }
3132
  $form['actions_configured'] = array(
3133
    '#type' => 'hidden',
3134
    '#value' => '1',
3135
  );
3136
  $form['actions'] = array('#type' => 'actions');
3137
  $form['actions']['submit'] = array(
3138
    '#type' => 'submit',
3139
    '#value' => t('Save'),
3140
    '#weight' => 13
3141
  );
3142
3143
  return $form;
3144
}
3145
3146
/**
3147
 * Validate system_actions_configure() form submissions.
3148
 */
3149
function system_actions_configure_validate($form, &$form_state) {
3150
  $function = actions_function_lookup($form_state['values']['actions_action']) . '_validate';
3151
  // Hand off validation to the action.
3152
  if (function_exists($function)) {
3153
    $function($form, $form_state);
3154
  }
3155
}
3156
3157
/**
3158
 * Process system_actions_configure() form submissions.
3159
 */
3160
function system_actions_configure_submit($form, &$form_state) {
3161
  $function = actions_function_lookup($form_state['values']['actions_action']);
3162
  $submit_function = $function . '_submit';
3163
3164
  // Action will return keyed array of values to store.
3165
  $params = $submit_function($form, $form_state);
3166
  $aid = isset($form_state['values']['actions_aid']) ? $form_state['values']['actions_aid'] : NULL;
3167
3168
  actions_save($function, $form_state['values']['actions_type'], $params, $form_state['values']['actions_label'], $aid);
3169
  drupal_set_message(t('The action has been successfully saved.'));
3170
3171
  $form_state['redirect'] = 'admin/config/system/actions/manage';
3172
}
3173
3174
/**
3175
 * Create the form for confirmation of deleting an action.
3176
 *
3177
 * @see system_actions_delete_form_submit()
3178
 * @ingroup forms
3179
 */
3180
function system_actions_delete_form($form, &$form_state, $action) {
3181
  $form['aid'] = array(
3182
    '#type' => 'hidden',
3183
    '#value' => $action->aid,
3184
  );
3185
  return confirm_form($form,
3186
    t('Are you sure you want to delete the action %action?', array('%action' => $action->label)),
3187
    'admin/config/system/actions/manage',
3188
    t('This cannot be undone.'),
3189
    t('Delete'),
3190
    t('Cancel')
3191
  );
3192
}
3193
3194
/**
3195
 * Process system_actions_delete form submissions.
3196
 *
3197
 * Post-deletion operations for action deletion.
3198
 */
3199
function system_actions_delete_form_submit($form, &$form_state) {
3200
  $aid = $form_state['values']['aid'];
3201
  $action = actions_load($aid);
3202
  actions_delete($aid);
3203
  watchdog('user', 'Deleted action %aid (%action)', array('%aid' => $aid, '%action' => $action->label));
3204
  drupal_set_message(t('Action %action was deleted', array('%action' => $action->label)));
3205
  $form_state['redirect'] = 'admin/config/system/actions/manage';
3206
}
3207
3208
/**
3209
 * Post-deletion operations for deleting action orphans.
3210
 *
3211
 * @param $orphaned
3212
 *   An array of orphaned actions.
3213
 */
3214
function system_action_delete_orphans_post($orphaned) {
3215
  foreach ($orphaned as $callback) {
3216
    drupal_set_message(t("Deleted orphaned action (%action).", array('%action' => $callback)));
3217
  }
3218
}
3219
3220
/**
3221
 * Remove actions that are in the database but not supported by any enabled module.
3222
 */
3223
function system_actions_remove_orphans() {
3224
  actions_synchronize(TRUE);
3225
  drupal_goto('admin/config/system/actions/manage');
3226
}