system.admin.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. <?php
  2. /**
  3. * @file
  4. * Admin page callbacks for the system module.
  5. */
  6. use Drupal\Component\Utility\Html;
  7. use Drupal\Core\Link;
  8. use Drupal\Core\Render\Element;
  9. use Drupal\Core\Template\Attribute;
  10. use Drupal\Core\Url;
  11. /**
  12. * Prepares variables for administrative content block templates.
  13. *
  14. * Default template: admin-block-content.html.twig.
  15. *
  16. * @param $variables
  17. * An associative array containing:
  18. * - content: An array containing information about the block. Each element
  19. * of the array represents an administrative menu item, and must at least
  20. * contain the keys 'title', 'link_path', and 'localized_options', which are
  21. * passed to l(). A 'description' key may also be provided.
  22. */
  23. function template_preprocess_admin_block_content(&$variables) {
  24. if (!empty($variables['content'])) {
  25. $variables['compact'] = system_admin_compact_mode();
  26. foreach ($variables['content'] as $key => $item) {
  27. $variables['content'][$key]['link'] = Link::fromTextAndUrl($item['title'], $item['url'])->toString();
  28. if (!$variables['compact'] && isset($item['description'])) {
  29. $variables['content'][$key]['description'] = ['#markup' => $item['description']];
  30. }
  31. else {
  32. $variables['content'][$key]['description'] = FALSE;
  33. }
  34. }
  35. }
  36. }
  37. /**
  38. * Prepares variables for administrative index page templates.
  39. *
  40. * Default template: admin-page.html.twig.
  41. *
  42. * @param $variables
  43. * An associative array containing:
  44. * - blocks: An array of blocks to display. Each array should include a
  45. * 'title', a 'description', a formatted 'content' and a 'position' which
  46. * will control which container it will be in. This is usually 'left' or
  47. * 'right'.
  48. */
  49. function template_preprocess_admin_page(&$variables) {
  50. $variables['system_compact_link'] = [
  51. '#type' => 'system_compact_link',
  52. ];
  53. $variables['containers'] = [];
  54. $stripe = 0;
  55. foreach ($variables['blocks'] as $block) {
  56. if (!empty($block['content']['#content'])) {
  57. if (empty($block['position'])) {
  58. // Perform automatic striping.
  59. $block['position'] = ++$stripe % 2 ? 'left' : 'right';
  60. }
  61. $variables['containers'][$block['position']]['blocks'][] = [
  62. '#theme' => 'admin_block',
  63. '#block' => $block,
  64. ];
  65. }
  66. }
  67. }
  68. /**
  69. * Prepares variables for admin index templates.
  70. *
  71. * Default template: system-admin-index.html.twig.
  72. *
  73. * @param $variables
  74. * An associative array containing:
  75. * - menu_items: An array of modules to be displayed.
  76. */
  77. function template_preprocess_system_admin_index(&$variables) {
  78. $variables['system_compact_link'] = [
  79. '#type' => 'system_compact_link',
  80. ];
  81. $variables['containers'] = [];
  82. $stripe = 0;
  83. // Iterate over all modules.
  84. foreach ($variables['menu_items'] as $module => $block) {
  85. list($description, $items) = $block;
  86. $position = ++$stripe % 2 ? 'left' : 'right';
  87. // Output links.
  88. if (count($items)) {
  89. $variables['containers'][$position][] = [
  90. '#theme' => 'admin_block',
  91. '#block' => [
  92. 'position' => $position,
  93. 'title' => $module,
  94. 'content' => [
  95. '#theme' => 'admin_block_content',
  96. '#content' => $items,
  97. ],
  98. 'description' => t($description),
  99. ],
  100. ];
  101. }
  102. }
  103. }
  104. /**
  105. * Prepares variables for the module details templates.
  106. *
  107. * Default template: system-modules-details.html.twig.
  108. *
  109. * @param $variables
  110. * An associative array containing:
  111. * - form: A render element representing the form. The main form element
  112. * represents a package, and child elements of the form are individual
  113. * projects. Each project (or module) is an associative array containing the
  114. * following elements:
  115. * - name: The name of the module.
  116. * - enable: A checkbox for enabling the module.
  117. * - description: A description of the module.
  118. * - version: The version of the module.
  119. * - links: Administration links provided by the module.
  120. * - #requires: A list of modules that the project requires.
  121. * - #required_by: A list of modules and themes that require the project.
  122. * - #attributes: A list of attributes for the module wrapper.
  123. *
  124. * @see \Drupal\system\Form\ModulesListForm
  125. */
  126. function template_preprocess_system_modules_details(&$variables) {
  127. $form = $variables['form'];
  128. // Identify modules that are depended on by themes.
  129. // Added here instead of ModuleHandler to avoid recursion.
  130. $themes = \Drupal::service('extension.list.theme')->getList();
  131. foreach ($themes as $theme) {
  132. foreach ($theme->info['dependencies'] as $dependency) {
  133. if (isset($form[$dependency])) {
  134. // Add themes to the module's required by list.
  135. $form[$dependency]['#required_by'][] = $theme->status ? t('@theme', ['@theme (theme)' => $theme->info['name']]) : t('@theme (theme) (<span class="admin-disabled">disabled</span>)', ['@theme' => $theme->info['name']]);
  136. }
  137. }
  138. }
  139. $variables['modules'] = [];
  140. // Iterate through all the modules, which are children of this element.
  141. foreach (Element::children($form) as $key) {
  142. // Stick the key into $module for easier access.
  143. $module = $form[$key];
  144. unset($module['enable']['#title']);
  145. $module['#requires'] = array_filter($module['#requires']);
  146. $module['#required_by'] = array_filter($module['#required_by']);
  147. // Add the checkbox to allow installing new modules and to show the
  148. // installation status of the module.
  149. $module['checkbox'] = $module['enable'];
  150. // Add the module label and expand/collapse functionality.
  151. $id = Html::getUniqueId('module-' . $key);
  152. $module['id'] = $id;
  153. $module['enable_id'] = $module['enable']['#id'];
  154. // @todo Remove early rendering and use safe_join in the Twig template once
  155. // https://www.drupal.org/node/2579091 is fixed.
  156. $renderer = \Drupal::service('renderer');
  157. $machine_name_render = [
  158. '#prefix' => '<span dir="ltr" class="table-filter-text-source">',
  159. '#plain_text' => $key,
  160. '#suffix' => '</span>',
  161. ];
  162. $module['machine_name'] = $renderer->render($machine_name_render);
  163. if (!empty($module['#requires'])) {
  164. $requires = [
  165. '#theme' => 'item_list',
  166. '#items' => $module['#requires'],
  167. '#context' => ['list_style' => 'comma-list'],
  168. ];
  169. $module['requires'] = $renderer->render($requires);
  170. }
  171. if (!empty($module['#required_by'])) {
  172. $required_by = [
  173. '#theme' => 'item_list',
  174. '#items' => $module['#required_by'],
  175. '#context' => ['list_style' => 'comma-list'],
  176. ];
  177. $module['required_by'] = $renderer->render($required_by);
  178. }
  179. if (!empty($module['version'])) {
  180. $module['version'] = $renderer->render($module['version']);
  181. }
  182. $module['attributes'] = new Attribute($module['#attributes']);
  183. $variables['modules'][] = $module;
  184. }
  185. }
  186. /**
  187. * Prepares variables for module uninstall templates.
  188. *
  189. * Default template: system-modules-uninstall.html.twig.
  190. *
  191. * @param $variables
  192. * An associative array containing:
  193. * - form: A render element representing the form. Child elements of the form
  194. * are individual modules. Each module is an associative array containing
  195. * the following elements:
  196. * - #module_name: The name of the module as a string.
  197. * - name: The name of the module in a renderable array.
  198. * - description: A description of the module.
  199. * - #required_by: (optional) A list of modules that require the module.
  200. * - #validation_reasons: (optional) Additional reasons why the module
  201. * cannot be uninstalled.
  202. * - #attributes: A list of attributes for the module wrapper.
  203. *
  204. * @ingroup themeable
  205. */
  206. function template_preprocess_system_modules_uninstall(&$variables) {
  207. $form = $variables['form'];
  208. $variables['modules'] = [];
  209. // Iterate through all the modules, which are children of this element.
  210. foreach (Element::children($form['modules']) as $key) {
  211. $module = $form['modules'][$key];
  212. $module['module_name'] = $module['#module_name'];
  213. $module['checkbox'] = $form['uninstall'][$key];
  214. $module['checkbox_id'] = $form['uninstall'][$key]['#id'];
  215. if (!empty($module['#validation_reasons'])) {
  216. $module['validation_reasons'] = $module['#validation_reasons'];
  217. $module['reasons_count'] = count($module['validation_reasons']);
  218. }
  219. else {
  220. $module['reasons_count'] = 0;
  221. }
  222. if (!empty($module['#required_by'])) {
  223. $module['required_by'] = $module['#required_by'];
  224. $module['reasons_count'] = $module['reasons_count'] + 1;
  225. }
  226. $module['attributes'] = new Attribute($module['#attributes']);
  227. $variables['modules'][] = $module;
  228. }
  229. }
  230. /**
  231. * Prepares variables for appearance page templates.
  232. *
  233. * Default template: system-themes-page.html.twig.
  234. *
  235. * @param $variables
  236. * An associative array containing:
  237. * - theme_groups: An associative array containing groups of themes.
  238. * - theme_group_titles: An associative array containing titles of themes.
  239. */
  240. function template_preprocess_system_themes_page(&$variables) {
  241. $groups = [];
  242. $theme_groups = $variables['theme_groups'];
  243. $variables['attributes']['id'] = 'system-themes-page';
  244. foreach ($variables['theme_group_titles'] as $state => $title) {
  245. if (!count($theme_groups[$state])) {
  246. // Skip this group of themes if no theme is there.
  247. continue;
  248. }
  249. // Start new theme group.
  250. $theme_group = [];
  251. $theme_group['state'] = $state;
  252. $theme_group['title'] = $title;
  253. $theme_group['themes'] = [];
  254. $theme_group['attributes'] = new Attribute();
  255. foreach ($theme_groups[$state] as $theme) {
  256. $current_theme = [];
  257. // Screenshot depicting the theme.
  258. if ($theme->screenshot) {
  259. $current_theme['screenshot'] = [
  260. '#theme' => 'image',
  261. '#uri' => $theme->screenshot['uri'],
  262. '#alt' => $theme->screenshot['alt'],
  263. '#title' => $theme->screenshot['title'],
  264. '#attributes' => $theme->screenshot['attributes'],
  265. ];
  266. }
  267. else {
  268. $current_theme['screenshot'] = [
  269. '#theme' => 'image',
  270. '#uri' => drupal_get_path('module', 'system') . '/images/no_screenshot.png',
  271. '#alt' => t('No screenshot'),
  272. '#title' => t('No screenshot'),
  273. '#attributes' => new Attribute(['class' => ['no-screenshot']]),
  274. ];
  275. }
  276. // Localize the theme description.
  277. $current_theme['description'] = t($theme->info['description']);
  278. $current_theme['attributes'] = new Attribute();
  279. $current_theme['name'] = $theme->info['name'];
  280. $current_theme['version'] = isset($theme->info['version']) ? $theme->info['version'] : '';
  281. $current_theme['notes'] = $theme->notes;
  282. $current_theme['is_default'] = $theme->is_default;
  283. $current_theme['is_admin'] = $theme->is_admin;
  284. $current_theme['module_dependencies'] = !empty($theme->module_dependencies_list) ? [
  285. '#theme' => 'item_list',
  286. '#items' => $theme->module_dependencies_list,
  287. '#context' => ['list_style' => 'comma-list'],
  288. ] : [];
  289. // Make sure to provide feedback on compatibility.
  290. $current_theme['incompatible'] = '';
  291. if (!empty($theme->info['core_incompatible'])) {
  292. $current_theme['incompatible'] = t("This theme is not compatible with Drupal @core_version. Check that the .info.yml file contains a compatible 'core' or 'core_version_requirement' value.", ['@core_version' => \Drupal::VERSION]);
  293. }
  294. elseif (!empty($theme->incompatible_region)) {
  295. $current_theme['incompatible'] = t("This theme is missing a 'content' region.");
  296. }
  297. elseif (!empty($theme->incompatible_php)) {
  298. if (substr_count($theme->info['php'], '.') < 2) {
  299. $theme->info['php'] .= '.*';
  300. }
  301. $current_theme['incompatible'] = t('This theme requires PHP version @php_required and is incompatible with PHP version @php_version.', ['@php_required' => $theme->info['php'], '@php_version' => phpversion()]);
  302. }
  303. elseif (!empty($theme->incompatible_base)) {
  304. $current_theme['incompatible'] = t('This theme requires the base theme @base_theme to operate correctly.', ['@base_theme' => $theme->info['base theme']]);
  305. }
  306. elseif (!empty($theme->incompatible_engine)) {
  307. $current_theme['incompatible'] = t('This theme requires the theme engine @theme_engine to operate correctly.', ['@theme_engine' => $theme->info['engine']]);
  308. }
  309. elseif (!empty($theme->incompatible_module)) {
  310. $current_theme['incompatible'] = t('This theme requires the listed modules to operate correctly.');
  311. }
  312. elseif (!empty($theme->module_dependencies_disabled)) {
  313. if (!empty($theme->insufficient_module_permissions)) {
  314. $current_theme['incompatible'] = t('This theme requires the listed modules to operate correctly. They must first be enabled by a user with permissions to do so.');
  315. }
  316. else {
  317. $modules_url = (string) Url::fromRoute('system.modules_list')->toString();
  318. $current_theme['incompatible'] = t('This theme requires the listed modules to operate correctly. They must first be enabled via the <a href=":modules_url">Extend page</a>.', [
  319. ':modules_url' => $modules_url,
  320. ]);
  321. }
  322. }
  323. // Build operation links.
  324. $current_theme['operations'] = [
  325. '#theme' => 'links',
  326. '#links' => $theme->operations,
  327. '#attributes' => [
  328. 'class' => ['operations', 'clearfix'],
  329. ],
  330. ];
  331. $theme_group['themes'][] = $current_theme;
  332. }
  333. $groups[] = $theme_group;
  334. }
  335. $variables['theme_groups'] = $groups;
  336. }