system.admin.inc 20 KB
Newer Older
1
2
3
<?php

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

8
use Drupal\Core\Cache\Cache;
9
use Symfony\Component\HttpFoundation\RedirectResponse;
10
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
11

12
13
14
15
/**
 * Menu callback; Set the default theme.
 */
function system_theme_default() {
16
  $request = \Drupal::request();
17
  $theme = $request->get('theme');
18
  if (!empty($theme)) {
19
    // Get current list of themes.
20
    $themes = list_themes();
21
22
23
24
25
26
27
28

    // Check if the specified theme is one recognized by the system.
    if (!empty($themes[$theme])) {
      // Enable the theme if it is currently disabled.
      if (empty($themes[$theme]->status)) {
       theme_enable(array($theme));
      }
      // Set the default theme.
29
      \Drupal::config('system.theme')
30
31
        ->set('default', $theme)
        ->save();
32

33
34
35
      // Rebuild the menu. This duplicates the menu_router_rebuild() in
      // theme_enable(). However, modules must know the current default theme in
      // order to use this information in hook_menu() or hook_menu_alter()
36
      // implementations, and saving the configuration before the theme_enable()
37
38
      // could result in a race condition where the theme is default but not
      // enabled.
39
      \Drupal::service('router.builder')->rebuild();
40
      menu_router_rebuild();
41
      Cache::deleteTags(array('local_task' => TRUE));
42

43
44
      // The status message depends on whether an admin theme is currently in use:
      // a value of 0 means the admin theme is set to be the default theme.
45
      $admin_theme = \Drupal::config('system.theme')->get('admin');
46
      if ($admin_theme != 0 && $admin_theme != $theme) {
47
48
49
50
51
52
53
54
55
56
57
58
        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(
          '%admin_theme' => $themes[$admin_theme]->info['name'],
          '%selected_theme' => $themes[$theme]->info['name'],
        )));
      }
      else {
        drupal_set_message(t('%theme is now the default theme.', array('%theme' => $themes[$theme]->info['name'])));
      }
    }
    else {
      drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
    }
59
    return new RedirectResponse(url('admin/appearance', array('absolute' => TRUE)));
60
  }
61
  throw new AccessDeniedHttpException();
62
63
}

64
/**
65
66
67
 * Recursively check compatibility.
 *
 * @param $incompatible
68
69
 *   An associative array which at the end of the check contains all
 *   incompatible files as the keys, their values being TRUE.
70
71
72
73
74
 * @param $files
 *   The set of files that will be tested.
 * @param $file
 *   The file at which the check starts.
 * @return
75
76
 *   Returns TRUE if an incompatible file is found, NULL (no return value)
 *   otherwise.
77
78
79
80
81
 */
function _system_is_incompatible(&$incompatible, $files, $file) {
  if (isset($incompatible[$file->name])) {
    return TRUE;
  }
82
83
84
  // Recursively traverse required modules, looking for incompatible modules.
  foreach ($file->requires as $requires) {
    if (isset($files[$requires]) && _system_is_incompatible($incompatible, $files, $files[$requires])) {
85
86
87
88
89
90
      $incompatible[$file->name] = TRUE;
      return TRUE;
    }
  }
}

91
/**
92
 * Returns HTML for an administrative block for display.
93
 *
94
95
 * @param $variables
 *   An associative array containing:
96
97
98
99
100
101
 *   - block: An array containing information about the block:
 *     - show: A Boolean whether to output the block. Defaults to FALSE.
 *     - title: The block's title.
 *     - content: (optional) Formatted content for the block.
 *     - description: (optional) Description of the block. Only output if
 *       'content' is not set.
102
 *
103
 * @ingroup themeable
104
 */
105
106
function theme_admin_block($variables) {
  $block = $variables['block'];
107
  $output = '';
108

109
  // Don't display the block if it has no content to display.
110
  if (empty($block['show'])) {
111
    return $output;
112
113
  }

114
115
116
117
118
  $output .= '<div class="admin-panel">';
  if (!empty($block['title'])) {
    $output .= '<h3>' . $block['title'] . '</h3>';
  }
  if (!empty($block['content'])) {
119
    $output .= '<div class="body">' . render($block['content']) . '</div>';
120
121
  }
  else {
122
    $output .= '<div class="description">' . $block['description'] . '</div>';
123
  }
124
125
  $output .= '</div>';

126
127
128
129
  return $output;
}

/**
130
 * Returns HTML for the content of an administrative block.
131
 *
132
133
 * @param $variables
 *   An associative array containing:
134
135
136
137
 *   - content: An array containing information about the block. Each element
 *     of the array represents an administrative menu item, and must at least
 *     contain the keys 'title', 'href', and 'localized_options', which are
 *     passed to l(). A 'description' key may also be provided.
138
 *
139
 * @ingroup themeable
140
 */
141
142
function theme_admin_block_content($variables) {
  $content = $variables['content'];
143
  $output = '';
144

145
146
147
148
  if (!empty($content)) {
    $class = 'admin-list';
    if ($compact = system_admin_compact_mode()) {
      $class .= ' compact';
149
    }
150
    $output .= '<dl class="' . $class . '">';
151
    foreach ($content as $item) {
152
      $output .= '<dt>' . l($item['title'], $item['href'], $item['localized_options']) . '</dt>';
153
154
155
      if (!$compact && isset($item['description'])) {
        $output .= '<dd>' . filter_xss_admin($item['description']) . '</dd>';
      }
156
157
158
159
160
161
162
    }
    $output .= '</dl>';
  }
  return $output;
}

/**
163
 * Returns HTML for an administrative page.
164
 *
165
166
167
 * @param $variables
 *   An associative array containing:
 *   - blocks: An array of blocks to display. Each array should include a
168
169
170
 *     'title', a 'description', a formatted 'content' and a 'position' which
 *     will control which container it will be in. This is usually 'left' or
 *     'right'.
171
 *
172
 * @ingroup themeable
173
 */
174
175
176
function theme_admin_page($variables) {
  $blocks = $variables['blocks'];

177
178
179
180
  $stripe = 0;
  $container = array();

  foreach ($blocks as $block) {
181
182
183
184
185
    $admin_block = array(
      '#theme' => 'admin_block',
      '#block' => $block,
    );
    if ($block_output = drupal_render($admin_block)) {
186
187
188
189
190
191
192
193
194
195
196
      if (empty($block['position'])) {
        // perform automatic striping.
        $block['position'] = ++$stripe % 2 ? 'left' : 'right';
      }
      if (!isset($container[$block['position']])) {
        $container[$block['position']] = '';
      }
      $container[$block['position']] .= $block_output;
    }
  }

197
  $system_compact_link = array('#theme' => 'system_compact_link');
198
  $output = '<div class="admin clearfix">';
199
  $output .= drupal_render($system_compact_link);
200
201

  foreach ($container as $id => $data) {
202
    $output .= '<div class="' . $id . ' clearfix">';
203
204
205
206
207
208
209
210
    $output .= $data;
    $output .= '</div>';
  }
  $output .= '</div>';
  return $output;
}

/**
211
 * Returns HTML for the output of the admin index page.
212
 *
213
214
215
216
 * @param $variables
 *   An associative array containing:
 *   - menu_items: An array of modules to be displayed.
 *
217
 * @ingroup themeable
218
 */
219
function theme_system_admin_index($variables) {
220
221
  $menu_items = $variables['menu_items'];

222
223
224
225
  $container = array('left' => '', 'right' => '');
  $flip = array('left' => 'right', 'right' => 'left');
  $position = 'left';

226
  // Iterate over all modules.
227
228
229
  foreach ($menu_items as $module => $block) {
    list($description, $items) = $block;

230
    // Output links.
231
    if (count($items)) {
232
233
234
235
      $admin_block_content = array(
        '#theme' => 'admin_block_content',
        '#content' => $items,
      );
236
237
      $block = array();
      $block['title'] = $module;
238
      $block['content'] = drupal_render($admin_block_content);
239
      $block['description'] = t($description);
240
      $block['show'] = TRUE;
241

242
243
244
245
246
      $admin_block = array(
        '#theme' => 'admin_block',
        '#block' => $block,
      );
      if ($block_output = drupal_render($admin_block)) {
247
248
249
250
251
252
253
254
255
256
        if (!isset($block['position'])) {
          // Perform automatic striping.
          $block['position'] = $position;
          $position = $flip[$position];
        }
        $container[$block['position']] .= $block_output;
      }
    }
  }

257
  $system_compact_link = array('#theme' => 'system_compact_link');
258
  $output = '<div class="admin clearfix">';
259
  $output .= drupal_render($system_compact_link);
260
  foreach ($container as $id => $data) {
261
    $output .= '<div class="' . $id . ' clearfix">';
262
263
264
265
266
267
268
269
270
    $output .= $data;
    $output .= '</div>';
  }
  $output .= '</div>';

  return $output;
}

/**
271
 * Returns HTML for the status report.
272
 *
273
274
275
 * This theme function is dependent on install.inc being loaded, because
 * that's where the constants are defined.
 *
276
277
 * @param $variables
 *   An associative array containing:
278
279
280
281
282
283
284
285
286
287
 *   - requirements: An array of requirements/status items. Each requirement
 *     is an associative array containing the following elements:
 *     - title: The name of the requirement.
 *     - value: (optional) The current value (version, time, level, etc).
 *     - description: (optional) The description of the requirement.
 *     - severity: (optional) The requirement's result/severity level, one of:
 *       - REQUIREMENT_INFO: Status information.
 *       - REQUIREMENT_OK: The requirement is satisfied.
 *       - REQUIREMENT_WARNING: The requirement failed with a warning.
 *       - REQUIREMENT_ERROR: The requirement failed with an error.
288
 *
289
290
 * @ingroup themeable
 */
291
292
function theme_status_report($variables) {
  $requirements = $variables['requirements'];
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
  $severities = array(
    REQUIREMENT_INFO => array(
      'title' => t('Info'),
      'class' => 'info',
    ),
    REQUIREMENT_OK => array(
      'title' => t('OK'),
      'class' => 'ok',
    ),
    REQUIREMENT_WARNING => array(
      'title' => t('Warning'),
      'class' => 'warning',
    ),
    REQUIREMENT_ERROR => array(
      'title' => t('Error'),
      'class' => 'error',
    ),
  );
311
312
313
  $output = '<table class="system-status-report"><thead><tr class="visually-hidden">';
  $output .= '<th>' . t('Status') . '</th><th>' . t('Component') . '</th><th>' . t('Details') . '</th>';
  $output .= '</tr></thead><tbody>';
314

315
  foreach ($requirements as $requirement) {
316
317
318
319
320
321
322
323
324
325
326
327
328
    // Always use the explicit requirement severity, if defined. Otherwise,
    // default to REQUIREMENT_OK in the installer to visually confirm that
    // installation requirements are met. And default to REQUIREMENT_INFO to
    // denote neutral information without special visualization.
    if (isset($requirement['severity'])) {
      $severity = $severities[(int) $requirement['severity']];
    }
    elseif (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install') {
      $severity = $severities[REQUIREMENT_OK];
    }
    else {
      $severity = $severities[REQUIREMENT_INFO];
    }
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
    // hook_requirements does not require value to be set. Set to null if not:
    if (isset($requirement['value'])) {
      $value = $requirement['value'];
    }
    else {
      $value = NULL;
    }

    $severity['icon'] = '<div title="' . $severity['title'] . '"><span class="visually-hidden">' . $severity['title'] . '</span></div>';

    // Output table rows.
    $output .= '<tr class="' . $severity['class'] . '">';
    $output .= '<td class="status-icon">' . $severity['icon'] . '</td>';
    $output .= '<td class="status-title">' . $requirement['title'] . '</td>';
    $output .= '<td class="status-value">' . $value;
    if (!empty($requirement['description'])) {
      $output .= '<div class="description">' . $requirement['description'] . '</div>';
    }
    $output .= '</td></tr>';
348
  }
349
350
351

  $output .= '</tbody></table>';
  return $output;
352
353
354
}

/**
355
 * Returns HTML for the modules form.
356
 *
357
358
 * @param $variables
 *   An associative array containing:
359
 *   - form: A render element representing the form.
360
 *
361
362
 * @ingroup themeable
 */
363
function theme_system_modules_details($variables) {
364
365
  $form = $variables['form'];

366
  // Individual table headers.
367
  $rows = array();
368
  // Iterate through all the modules, which are children of this element.
369
  foreach (element_children($form) as $key) {
370
    // Stick the key into $module for easier access.
371
    $module = $form[$key];
372
    // Create the row for the table.
373
    $row = array();
374
    // Add the checkbox into the first cell.
375
    unset($module['enable']['#title']);
376
377
378
379
380
381
382
    $module['#requires'] = array_filter($module['#requires']);
    $module['#required_by'] = array_filter($module['#required_by']);

    $requires = !empty($module['#requires']);
    $required_by = !empty($module['#required_by']);
    $version = !empty($module['version']['#markup']);

383
    $row[] = array('class' => array('checkbox'), 'data' => drupal_render($module['enable']));
384
385

    // Add the module label and expand/collapse functionalty.
386
    $col2 = '<label id="module-' . $key . '" for="' . $module['enable']['#id'] . '" class="module-name table-filter-text-source">' . drupal_render($module['name']) . '</label>';
387
388
    $row[] = array('class' => array('module'), 'data' => $col2);

389
    // Add the description, along with any modules it requires.
390
    $description = '';
391
392
393
394
395
396
397
398
399
400
401
402
    if ($version || $requires || $required_by) {
      $description .= ' <div class="requirements">';
      if ($version) {
        $description .= '<div class="admin-requirements">' . t('Version: !module-version', array('!module-version' => drupal_render($module['version']))) . '</div>';
      }
      if ($requires) {
        $description .= '<div class="admin-requirements">' . t('Requires: !module-list', array('!module-list' => implode(', ', $module['#requires']))) . '</div>';
      }
      if ($required_by) {
        $description .= '<div class="admin-requirements">' . t('Required by: !module-list', array('!module-list' => implode(', ', $module['#required_by']))) . '</div>';
      }
      $description .= '</div>';
403
    }
404
    $links = '';
405
    foreach (array('help', 'permissions', 'configure') as $key) {
406
      $links .= drupal_render($module['links'][$key]);
407
    }
408
409
410
411
412
    if ($links) {
      $description .= '  <div class="links">';
      $description .= $links;
      $description .= '</div>';
    }
413
414
415
416
417
418
419
420
    $details = array(
      '#type' => 'details',
      '#title' => '<span class="text"> ' . drupal_render($module['description']) . '</span>',
      '#attributes' => array('id' => $module['enable']['#id'] . '-description'),
      '#description' => $description,
      '#collapsed' => TRUE,
    );
    $col4 = drupal_render($details);
421
422
    $row[] = array('class' => array('description', 'expand'), 'data' => $col4);

423
    $rows[] = $row;
424
425
  }

426
427
428
429
430
431
  $table = array(
    '#theme' => 'table',
    '#header' => $form['#header'],
    '#rows' => $rows,
  );
  return drupal_render($table);
432
433
434
}

/**
435
 * Returns HTML for a message about incompatible modules.
436
437
438
439
440
 *
 * @param $variables
 *   An associative array containing:
 *   - message: The form array representing the currently disabled modules.
 *
441
 * @ingroup themeable
442
 */
443
444
function theme_system_modules_incompatible($variables) {
  return '<div class="incompatible">' . $variables['message'] . '</div>';
445
446
447
}

/**
448
 * Returns HTML for a table of currently disabled modules.
449
450
451
 *
 * @param $variables
 *   An associative array containing:
452
 *   - form: A render element representing the form.
453
 *
454
 * @ingroup themeable
455
 */
456
457
458
function theme_system_modules_uninstall($variables) {
  $form = $variables['form'];

459
460
461
462
463
464
465
466
467
468
469
470
471
472
  // No theming for the confirm form.
  if (isset($form['confirm'])) {
    return drupal_render($form);
  }

  // Table headers.
  $header = array(t('Uninstall'),
    t('Name'),
    t('Description'),
  );

  // Display table.
  $rows = array();
  foreach (element_children($form['modules']) as $module) {
473
474
    if (!empty($form['modules'][$module]['#required_by'])) {
      $disabled_message = format_plural(count($form['modules'][$module]['#required_by']),
475
476
        'To uninstall @module, the following module must be uninstalled first: @required_modules',
        'To uninstall @module, the following modules must be uninstalled first: @required_modules',
477
        array('@module' => $form['modules'][$module]['#module_name'], '@required_modules' => implode(', ', $form['modules'][$module]['#required_by'])));
478
479
480
481
482
      $disabled_message = '<div class="admin-requirements">' . $disabled_message . '</div>';
    }
    else {
      $disabled_message = '';
    }
483
484
    $rows[] = array(
      array('data' => drupal_render($form['uninstall'][$module]), 'align' => 'center'),
485
      '<strong><label for="' . $form['uninstall'][$module]['#id'] . '">' . drupal_render($form['modules'][$module]['name']) . '</label></strong>',
486
      array('data' => drupal_render($form['modules'][$module]['description']) . $disabled_message, 'class' => array('description')),
487
488
489
    );
  }

490
491
492
493
494
495
496
  $table = array(
    '#theme' => 'table',
    '#header' => $header,
    '#rows' => $rows,
    '#empty' => t('No modules are available to uninstall.'),
  );
  $output  = drupal_render($table);
497
  $output .= drupal_render_children($form);
498
499
500
501
502

  return $output;
}

/**
503
 * Returns HTML for the Appearance page.
504
 *
505
506
 * @param $variables
 *   An associative array containing:
507
 *   - theme_groups: An associative array containing groups of themes.
508
 *   - theme_group_titles: An associative array containing titles of themes.
509
 *
510
511
 * @ingroup themeable
 */
512
513
function theme_system_themes_page($variables) {
  $theme_groups = $variables['theme_groups'];
514

515
516
517
518
519
  $output = '<div id="system-themes-page">';

  foreach ($variables['theme_group_titles'] as $state => $title) {
    if (!count($theme_groups[$state])) {
      // Skip this group of themes if no theme is there.
520
521
      continue;
    }
522
523
    // Start new theme group.
    $output .= '<div class="system-themes-list system-themes-list-'. $state .' clearfix"><h2>'. $title .'</h2>';
524

525
    foreach ($theme_groups[$state] as $theme) {
526

527
      // Theme the screenshot.
528
529
530
531
532
533
534
535
536
537
538
539
540
      if ($theme->screenshot) {
        $image = array(
          '#theme' => 'image',
          '#uri' => $theme->screenshot['uri'],
          '#alt' => $theme->screenshot['alt'],
          '#title' => $theme->screenshot['title'],
          '#attributes' => $theme->screenshot['attributes'],
        );
        $screenshot = drupal_render($image);
      }
      else {
        $screenshot = '<div class="no-screenshot"><div class="no-screenshot__text">' . t('no screenshot') . '</div></div>';
      }
541

542
543
544
545
546
547
548
549
550
551
552
      // Localize the theme description.
      $description = t($theme->info['description']);

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

      // Make sure to provide feedback on compatibility.
      if (!empty($theme->incompatible_core)) {
553
        $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>';
554
555
556
557
558
559
560
      }
      elseif (!empty($theme->incompatible_php)) {
        if (substr_count($theme->info['php'], '.') < 2) {
          $theme->info['php'] .= '.*';
        }
        $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>';
      }
561
562
563
564
565
566
      elseif (!empty($theme->incompatible_base)) {
        $output .= '<div class="incompatible">' . t('This theme requires the base theme @base_theme to operate correctly.', array('@base_theme' => $theme->info['base theme'])) . '</div>';
      }
      elseif (!empty($theme->incompatible_engine)) {
        $output .= '<div class="incompatible">' . t('This theme requires the theme engine @theme_engine to operate correctly.', array('@theme_engine' => $theme->info['engine'])) . '</div>';
      }
567
      else {
568
569
570
571
572
573
574
575
        $links = array(
          '#theme' => 'links',
          '#links' => $theme->operations,
          '#attributes' => array(
            'class' => array('operations', 'clearfix'),
          ),
        );
        $output .= drupal_render($links);
576
577
      }
      $output .= '</div></div>';
578
    }
579
    $output .= '</div>';
580
  }
581
  $output .= '</div>';
582
583

  return $output;
584
}