Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / system / system.module
1 <?php
2
3 /**
4  * @file
5  * Configuration system that lets administrators modify the workings of the site.
6  */
7
8 use Drupal\Component\Render\PlainTextOutput;
9 use Drupal\Component\Utility\UrlHelper;
10 use Drupal\Core\Asset\AttachedAssetsInterface;
11 use Drupal\Core\Cache\Cache;
12 use Drupal\Core\Queue\QueueGarbageCollectionInterface;
13 use Drupal\Core\Database\Query\AlterableInterface;
14 use Drupal\Core\Extension\Extension;
15 use Drupal\Core\Extension\ExtensionDiscovery;
16 use Drupal\Core\Form\FormStateInterface;
17 use Drupal\Core\KeyValueStore\KeyValueDatabaseExpirableFactory;
18 use Drupal\Core\PageCache\RequestPolicyInterface;
19 use Drupal\Core\PhpStorage\PhpStorageFactory;
20 use Drupal\Core\Routing\RouteMatchInterface;
21 use Drupal\Core\Routing\StackedRouteMatchInterface;
22 use Drupal\Core\Language\LanguageInterface;
23 use Drupal\Core\Menu\MenuTreeParameters;
24 use Drupal\Core\Extension\ModuleHandler;
25 use Drupal\Core\Url;
26 use Drupal\Core\Block\BlockPluginInterface;
27 use Drupal\user\UserInterface;
28 use Symfony\Component\HttpFoundation\RedirectResponse;
29 use GuzzleHttp\Exception\RequestException;
30
31 /**
32  * New users will be set to the default time zone at registration.
33  *
34  * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
35  *   Use \Drupal\user\UserInterface::TIMEZONE_DEFAULT instead.
36  *
37  * @see https://www.drupal.org/node/2831620
38  */
39 const DRUPAL_USER_TIMEZONE_DEFAULT = 0;
40
41 /**
42  * New users will get an empty time zone at registration.
43  *
44  * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
45  *   Use \Drupal\user\UserInterface::TIMEZONE_EMPTY instead.
46  *
47  * @see https://www.drupal.org/node/2831620
48  */
49 const DRUPAL_USER_TIMEZONE_EMPTY = 1;
50
51 /**
52  * New users will select their own timezone at registration.
53  *
54  * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
55  *   Use \Drupal\user\UserInterface::TIMEZONE_SELECT instead.
56  *
57  * @see https://www.drupal.org/node/2831620
58  */
59 const DRUPAL_USER_TIMEZONE_SELECT = 2;
60
61 /**
62  * Disabled option on forms and settings
63  */
64 const DRUPAL_DISABLED = 0;
65
66 /**
67  * Optional option on forms and settings
68  */
69 const DRUPAL_OPTIONAL = 1;
70
71 /**
72  * Required option on forms and settings
73  */
74 const DRUPAL_REQUIRED = 2;
75
76 /**
77  * Return only visible regions.
78  *
79  * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
80  *   Use \Drupal\block\BlockRepositoryInterface::REGIONS_VISIBLE instead.
81  *
82  * @see system_region_list()
83  * @see https://www.drupal.org/node/2831620
84  */
85 const REGIONS_VISIBLE = 'visible';
86
87 /**
88  * Return all regions.
89  *
90  * @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
91  *   Use \Drupal\block\BlockRepositoryInterface::REGIONS_ALL instead.
92  *
93  * @see system_region_list()
94  * @see https://www.drupal.org/node/2831620
95  */
96 const REGIONS_ALL = 'all';
97
98 /**
99  * Implements hook_help().
100  */
101 function system_help($route_name, RouteMatchInterface $route_match) {
102   switch ($route_name) {
103     case 'help.page.system':
104       $output = '';
105       $output .= '<h3>' . t('About') . '</h3>';
106       $output .= '<p>' . t('The System module is integral to the site: it provides user interfaces for many core systems and settings, as well as the basic administrative menu structure. For more information, see the <a href=":system">online documentation for the System module</a>.', [':system' => 'https://www.drupal.org/documentation/modules/system']) . '</p>';
107       $output .= '<h3>' . t('Uses') . '</h3>';
108       $output .= '<dl>';
109       $output .= '<dt>' . t('Managing modules') . '</dt>';
110       $output .= '<dd>' . t('Users with appropriate permission can install and uninstall modules from the <a href=":modules">Extend page</a>. Depending on which distribution or installation profile you choose when you install your site, several modules are installed and others are provided but not installed. Each module provides a discrete set of features; modules may be installed or uninstalled depending on the needs of the site. Many additional modules contributed by members of the Drupal community are available for download from the <a href=":drupal-modules">Drupal.org module page</a>. Note that uninstalling a module is a destructive action: when you uninstall a module, you will permanently lose all data connected to the module.', [':modules' => \Drupal::url('system.modules_list'), ':drupal-modules' => 'https://www.drupal.org/project/modules']) . '</dd>';
111       $output .= '<dt>' . t('Managing themes') . '</dt>';
112       $output .= '<dd>' . t('Users with appropriate permission can install and uninstall themes on the <a href=":themes">Appearance page</a>. Themes determine the design and presentation of your site. Depending on which distribution or installation profile you choose when you install your site, a default theme is installed, and possibly a different theme for administration pages. Other themes are provided but not installed, and additional contributed themes are available at the <a href=":drupal-themes">Drupal.org theme page</a>.', [':themes' => \Drupal::url('system.themes_page'), ':drupal-themes' => 'https://www.drupal.org/project/themes']) . '</dd>';
113       $output .= '<dt>' . t('Disabling drag-and-drop functionality') . '</dt>';
114       $output .= '<dd>' . t('The default drag-and-drop user interface for ordering tables in the administrative interface presents a challenge for some users, including users of screen readers and other assistive technology. The drag-and-drop interface can be disabled in a table by clicking a link labeled "Show row weights" above the table. The replacement interface allows users to order the table by choosing numerical weights instead of dragging table rows.') . '</dd>';
115       $output .= '<dt>' . t('Configuring basic site settings') . '</dt>';
116       $output .= '<dd>' . t('The System module provides pages for managing basic site configuration, including <a href=":date-time-settings">Date and time formats</a> and <a href=":site-info">Basic site settings</a> (site name, email address to send mail from, home page, and error pages). Additional configuration pages are listed on the main <a href=":config">Configuration page</a>.', [':date-time-settings' => \Drupal::url('entity.date_format.collection'), ':site-info' => \Drupal::url('system.site_information_settings'), ':config' => \Drupal::url('system.admin_config')]) . '</dd>';
117       $output .= '<dt>' . t('Checking site status') . '</dt>';
118       $output .= '<dd>' . t('The <a href=":status">Status report</a> provides an overview of the configuration, status, and health of your site. Review this report to make sure there are not any problems to address, and to find information about the software your site and web server are using.', [':status' => \Drupal::url('system.status')]) . '</dd>';
119       $output .= '<dt>' . t('Using maintenance mode') . '</dt>';
120       $output .= '<dd>' . t('When you are performing site maintenance, you can prevent non-administrative users (including anonymous visitors) from viewing your site by putting it in <a href=":maintenance-mode">Maintenance mode</a>. This will prevent unauthorized users from making changes to the site while you are performing maintenance, or from seeing a broken site while updates are in progress.', [':maintenance-mode' => \Drupal::url('system.site_maintenance_mode')]) . '</dd>';
121       $output .= '<dt>' . t('Configuring for performance') . '</dt>';
122       $output .= '<dd>' . t('On the <a href=":performance-page">Performance page</a>, the site can be configured to aggregate CSS and JavaScript files, making the total request size smaller. Note that, for small- to medium-sized websites, the <a href=":page-cache">Internal Page Cache module</a> should be installed so that pages are efficiently cached and reused for anonymous users. Finally, for websites of all sizes, the <a href=":dynamic-page-cache">Dynamic Page Cache module</a> should also be installed so that the non-personalized parts of pages are efficiently cached (for all users).', [':performance-page' => \Drupal::url('system.performance_settings'), ':page-cache' => (\Drupal::moduleHandler()->moduleExists('page_cache')) ? \Drupal::url('help.page', ['name' => 'page_cache']) : '#', ':dynamic-page-cache' => (\Drupal::moduleHandler()->moduleExists('dynamic_page_cache')) ? \Drupal::url('help.page', ['name' => 'dynamic_page_cache']) : '#']) . '</dd>';
123       $output .= '<dt>' . t('Configuring cron') . '</dt>';
124       $output .= '<dd>' . t('In order for the site and its modules to continue to operate well, a set of routine administrative operations must run on a regular basis; these operations are known as <em>cron</em> tasks. On the <a href=":cron">Cron page</a>, you can configure cron to run periodically as part of server responses by installing the <em>Automated Cron</em> module, or you can turn this off and trigger cron from an outside process on your web server. You can verify the status of cron tasks by visiting the <a href=":status">Status report page</a>. For more information, see the <a href=":handbook">online documentation for configuring cron jobs</a>.', [':status' => \Drupal::url('system.status'), ':handbook' => 'https://www.drupal.org/cron', ':cron' => \Drupal::url('system.cron_settings')]) . '</dd>';
125       $output .= '<dt>' . t('Configuring the file system') . '</dt>';
126       $output .= '<dd>' . t('Your site has several file directories, which are used to store and process uploaded and generated files. The <em>public</em> file directory, which is configured in your settings.php file, is the default place for storing uploaded files. Links to files in this directory contain the direct file URL, so when the files are requested, the web server will send them directly without invoking your site code. This means that the files can be downloaded by anyone with the file URL, so requests are not access-controlled but they are efficient. The <em>private</em> file directory, also configured in your settings.php file and ideally located outside the site web root, is access controlled. Links to files in this directory are not direct, so requests to these files are mediated by your site code. This means that your site can check file access permission for each file before deciding to fulfill the request, so the requests are more secure, but less efficient. You should only use the private storage for files that need access control, not for files like your site logo and background images used on every page. The <em>temporary</em> file directory is used internally by your site code for various operations, and is configured on the <a href=":file-system">File system settings</a> page. You can also see the configured public and private file directories on this page, and choose whether public or private should be the default for uploaded files.', [':file-system' => \Drupal::url('system.file_system_settings')]) . '</dd>';
127       $output .= '<dt>' . t('Configuring the image toolkit') . '</dt>';
128       $output .= '<dd>' . t('On the <a href=":toolkit">Image toolkit page</a>, you can select and configure the PHP toolkit used to manipulate images. Depending on which distribution or installation profile you choose when you install your site, the GD2 toolkit and possibly others are included; other toolkits may be provided by contributed modules.', [':toolkit' => \Drupal::url('system.image_toolkit_settings')]) . '</dd>';
129       $output .= '</dl>';
130       return $output;
131
132     case 'system.admin_index':
133       return '<p>' . t('This page shows you all available administration tasks for each module.') . '</p>';
134
135     case 'system.themes_page':
136       $output = '<p>' . t('Set and configure the default theme for your website.  Alternative <a href=":themes">themes</a> are available.', [':themes' => 'https://www.drupal.org/project/themes']) . '</p>';
137       if (\Drupal::moduleHandler()->moduleExists('block')) {
138         $output .= '<p>' . t('You can place blocks for each theme on the <a href=":blocks">block layout</a> page.', [':blocks' => \Drupal::url('block.admin_display')]) . '</p>';
139       }
140       return $output;
141
142     case 'system.theme_settings_theme':
143       $theme_list = \Drupal::service('theme_handler')->listInfo();
144       $theme = $theme_list[$route_match->getParameter('theme')];
145       return '<p>' . t('These options control the display settings for the %name theme. When your site is displayed using this theme, these settings will be used.', ['%name' => $theme->info['name']]) . '</p>';
146
147     case 'system.theme_settings':
148       return '<p>' . t('Control default display settings for your site, across all themes. Use theme-specific settings to override these defaults.') . '</p>';
149
150     case 'system.modules_list':
151       $output = '<p>' . t('Download additional <a href=":modules">contributed modules</a> to extend your site\'s functionality.', [':modules' => 'https://www.drupal.org/project/modules']) . '</p>';
152       if (!\Drupal::moduleHandler()->moduleExists('update')) {
153         $output .= '<p>' . t('Regularly review available updates to maintain a secure and current site. Always run the <a href=":update-php">update script</a> each time a module is updated. Enable the <a href=":update-manager">Update Manager module</a> to update and install modules and themes.', [':update-php' => \Drupal::url('system.db_update'), ':update-manager' => \Drupal::url('system.modules_list', [], ['fragment' => 'module-update'])]) . '</p>';
154       }
155       return $output;
156
157     case 'system.modules_uninstall':
158       return '<p>' . t('The uninstall process removes all data related to a module.') . '</p>';
159
160     case 'entity.block.edit_form':
161       if (($block = $route_match->getParameter('block')) && $block->getPluginId() == 'system_powered_by_block') {
162         return '<p>' . t('The <em>Powered by Drupal</em> block is an optional link to the home page of the Drupal project. While there is absolutely no requirement that sites feature this link, it may be used to show support for Drupal.') . '</p>';
163       }
164       break;
165
166     case 'block.admin_add':
167       if ($route_match->getParameter('plugin_id') == 'system_powered_by_block') {
168         return '<p>' . t('The <em>Powered by Drupal</em> block is an optional link to the home page of the Drupal project. While there is absolutely no requirement that sites feature this link, it may be used to show support for Drupal.') . '</p>';
169       }
170       break;
171
172     case 'system.site_maintenance_mode':
173       if (\Drupal::currentUser()->id() == 1) {
174         return '<p>' . t('Use maintenance mode when making major updates, particularly if the updates could disrupt visitors or the update process. Examples include upgrading, importing or exporting content, modifying a theme, modifying content types, and making backups.') . '</p>';
175       }
176       break;
177
178     case 'system.status':
179       return '<p>' . t("Here you can find a short overview of your site's parameters as well as any problems detected with your installation. It may be useful to copy and paste this information into support requests filed on Drupal.org's support forums and project issue queues. Before filing a support request, ensure that your web server meets the <a href=\":system-requirements\">system requirements.</a>", [':system-requirements' => 'https://www.drupal.org/requirements']) . '</p>';
180   }
181 }
182
183 /**
184  * Implements hook_theme().
185  */
186 function system_theme() {
187   return array_merge(drupal_common_theme(), [
188     // Normally theme suggestion templates are only picked up when they are in
189     // themes. We explicitly define theme suggestions here so that the block
190     // templates in core/modules/system/templates are picked up.
191     'block__system_branding_block' => [
192       'render element' => 'elements',
193       'base hook' => 'block',
194     ],
195     'block__system_messages_block' => [
196       'base hook' => 'block',
197     ],
198     'block__system_menu_block' => [
199       'render element' => 'elements',
200       'base hook' => 'block',
201     ],
202     'system_themes_page' => [
203       'variables' => [
204         'theme_groups' => [],
205         'theme_group_titles' => [],
206       ],
207       'file' => 'system.admin.inc',
208     ],
209     'system_config_form' => [
210       'render element' => 'form',
211     ],
212     'confirm_form' => [
213       'render element' => 'form',
214     ],
215     'system_modules_details' => [
216       'render element' => 'form',
217       'file' => 'system.admin.inc',
218     ],
219     'system_modules_uninstall' => [
220       'render element' => 'form',
221       'file' => 'system.admin.inc',
222     ],
223     'status_report_page' => [
224       'variables' => [
225         'counters' => [],
226         'general_info' => [],
227         'requirements' => NULL,
228       ],
229     ],
230     'status_report' => [
231       'variables' => [
232         'grouped_requirements' => NULL,
233         'requirements' => NULL,
234       ],
235     ],
236     'status_report_grouped' => [
237       'variables' => [
238         'grouped_requirements' => NULL,
239         'requirements' => NULL,
240       ],
241     ],
242     'status_report_counter' => [
243       'variables' => ['amount' => NULL, 'text' => NULL, 'severity' => NULL],
244     ],
245     'status_report_general_info' => [
246       'variables' => [
247         'drupal' => [],
248         'cron' => [],
249         'database_system' => [],
250         'database_system_version' => [],
251         'php' => [],
252         'php_memory_limit' => [],
253         'webserver' => [],
254       ],
255     ],
256     'admin_page' => [
257       'variables' => ['blocks' => NULL],
258       'file' => 'system.admin.inc',
259     ],
260     'admin_block' => [
261       'variables' => ['block' => NULL],
262       'file' => 'system.admin.inc',
263     ],
264     'admin_block_content' => [
265       'variables' => ['content' => NULL],
266       'file' => 'system.admin.inc',
267     ],
268     'system_admin_index' => [
269       'variables' => ['menu_items' => NULL],
270       'file' => 'system.admin.inc',
271     ],
272     'entity_add_list' => [
273       'variables' => [
274         'bundles' => [],
275         'add_bundle_message' => NULL,
276       ],
277       'template' => 'entity-add-list',
278     ],
279   ]);
280 }
281
282 /**
283  * Implements hook_hook_info().
284  */
285 function system_hook_info() {
286   $hooks['token_info'] = [
287     'group' => 'tokens',
288   ];
289   $hooks['token_info_alter'] = [
290     'group' => 'tokens',
291   ];
292   $hooks['tokens'] = [
293     'group' => 'tokens',
294   ];
295   $hooks['tokens_alter'] = [
296     'group' => 'tokens',
297   ];
298
299   return $hooks;
300 }
301
302 /**
303  * Implements hook_theme_suggestions_HOOK().
304  */
305 function system_theme_suggestions_html(array $variables) {
306   $path_args = explode('/', trim(\Drupal::service('path.current')->getPath(), '/'));
307   return theme_get_suggestions($path_args, 'html');
308 }
309
310 /**
311  * Implements hook_theme_suggestions_HOOK().
312  */
313 function system_theme_suggestions_page(array $variables) {
314   $path_args = explode('/', trim(\Drupal::service('path.current')->getPath(), '/'));
315   return theme_get_suggestions($path_args, 'page');
316 }
317
318 /**
319  * Implements hook_theme_suggestions_HOOK().
320  */
321 function system_theme_suggestions_maintenance_page(array $variables) {
322   $suggestions = [];
323
324   // Dead databases will show error messages so supplying this template will
325   // allow themers to override the page and the content completely.
326   $offline = defined('MAINTENANCE_MODE');
327   try {
328     \Drupal::service('path.matcher')->isFrontPage();
329   }
330   catch (Exception $e) {
331     // The database is not yet available.
332     $offline = TRUE;
333   }
334   if ($offline) {
335     $suggestions[] = 'maintenance_page__offline';
336   }
337
338   return $suggestions;
339 }
340
341 /**
342  * Implements hook_theme_suggestions_HOOK().
343  */
344 function system_theme_suggestions_region(array $variables) {
345   $suggestions = [];
346   if (!empty($variables['elements']['#region'])) {
347     $suggestions[] = 'region__' . $variables['elements']['#region'];
348   }
349   return $suggestions;
350 }
351
352 /**
353  * Implements hook_theme_suggestions_HOOK().
354  */
355 function system_theme_suggestions_field(array $variables) {
356   $suggestions = [];
357   $element = $variables['element'];
358
359   $suggestions[] = 'field__' . $element['#field_type'];
360   $suggestions[] = 'field__' . $element['#field_name'];
361   $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#bundle'];
362   $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'];
363   $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'] . '__' . $element['#bundle'];
364
365   return $suggestions;
366 }
367
368 /**
369  * Prepares variables for the list of available bundles.
370  *
371  * Default template: entity-add-list.html.twig.
372  *
373  * @param array $variables
374  *   An associative array containing:
375  *   - bundles: An array of bundles with the label, description, add_link keys.
376  *   - add_bundle_message: The message shown when there are no bundles. Only
377  *     available if the entity type uses bundle entities.
378  */
379 function template_preprocess_entity_add_list(&$variables) {
380   foreach ($variables['bundles'] as $bundle_name => $bundle_info) {
381     $variables['bundles'][$bundle_name]['description'] = [
382       '#markup' => $bundle_info['description'],
383     ];
384   }
385 }
386
387 /**
388  * @defgroup authorize Authorized operations
389  * @{
390  * Functions to run operations with elevated privileges via authorize.php.
391  *
392  * Because of the Update manager functionality included in Drupal core, there
393  * is a mechanism for running operations with elevated file system privileges,
394  * the top-level authorize.php script. This script runs at a reduced Drupal
395  * bootstrap level so that it is not reliant on the entire site being
396  * functional. The operations use a FileTransfer class to manipulate code
397  * installed on the system as the user that owns the files, not the user that
398  * the httpd is running as.
399  *
400  * The first setup is to define a callback function that should be authorized
401  * to run with the elevated privileges. This callback should take a
402  * FileTransfer as its first argument, although you can define an array of
403  * other arguments it should be invoked with. The callback should be placed in
404  * a separate .inc file that will be included by authorize.php.
405  *
406  * To run the operation, certain data must be saved into the SESSION, and then
407  * the flow of control should be redirected to the authorize.php script. There
408  * are two ways to do this, either to call system_authorized_run() directly,
409  * or to call system_authorized_init() and then redirect to authorize.php,
410  * using the URL from system_authorized_get_url(). Redirecting yourself is
411  * necessary when your authorized operation is being triggered by a form
412  * submit handler, since calling redirecting in a submit handler is a bad
413  * idea, and you should instead use $form_state->setRedirect().
414  *
415  * Once the SESSION is setup for the operation and the user is redirected to
416  * authorize.php, they will be prompted for their connection credentials (core
417  * provides FTP and SSH by default, although other connection classes can be
418  * added via contributed modules). With valid credentials, authorize.php will
419  * instantiate the appropriate FileTransfer object, and then invoke the
420  * desired operation passing in that object. The authorize.php script can act
421  * as a Batch API processing page, if the operation requires a batch.
422  *
423  * @see authorize.php
424  * @see \Drupal\Core\FileTransfer\FileTransfer
425  * @see hook_filetransfer_info()
426  */
427
428 /**
429  * Setup a given callback to run via authorize.php with elevated privileges.
430  *
431  * To use authorize.php, certain variables must be stashed into $_SESSION. This
432  * function sets up all the necessary $_SESSION variables. The calling function
433  * should then redirect to authorize.php, using the full path returned by
434  * system_authorized_get_url(). That initiates the workflow that will eventually
435  * lead to the callback being invoked. The callback will be invoked at a low
436  * bootstrap level, without all modules being invoked, so it needs to be careful
437  * not to assume any code exists. Example (system_authorized_run()):
438  * @code
439  *   system_authorized_init($callback, $file, $arguments, $page_title);
440  *   return new RedirectResponse(system_authorized_get_url()->toString());
441  * @endcode
442  * Example (update_manager_install_form_submit()):
443  * @code
444  *  system_authorized_init('update_authorize_run_install',
445  *    drupal_get_path('module', 'update') . '/update.authorize.inc',
446  *    $arguments, t('Update manager'));
447  *  $form_state->setRedirectUrl(system_authorized_get_url());
448  * @endcode
449  *
450  * @param $callback
451  *   The name of the function to invoke once the user authorizes the operation.
452  * @param $file
453  *   The full path to the file where the callback function is implemented.
454  * @param $arguments
455  *   Optional array of arguments to pass into the callback when it is invoked.
456  *   Note that the first argument to the callback is always the FileTransfer
457  *   object created by authorize.php when the user authorizes the operation.
458  * @param $page_title
459  *   Optional string to use as the page title once redirected to authorize.php.
460  * @return
461  *   Nothing, this function just initializes variables in the user's session.
462  */
463 function system_authorized_init($callback, $file, $arguments = [], $page_title = NULL) {
464   // First, figure out what file transfer backends the site supports, and put
465   // all of those in the SESSION so that authorize.php has access to all of
466   // them via the class autoloader, even without a full bootstrap.
467   $_SESSION['authorize_filetransfer_info'] = drupal_get_filetransfer_info();
468
469   // Now, define the callback to invoke.
470   $_SESSION['authorize_operation'] = [
471     'callback' => $callback,
472     'file' => $file,
473     'arguments' => $arguments,
474   ];
475
476   if (isset($page_title)) {
477     $_SESSION['authorize_page_title'] = $page_title;
478   }
479 }
480
481 /**
482  * Return the URL for the authorize.php script.
483  *
484  * @param array $options
485  *   Optional array of options to set on the \Drupal\Core\Url object.
486  * @return \Drupal\Core\Url
487  *   The full URL to authorize.php, using HTTPS if available.
488  *
489  * @see system_authorized_init()
490  */
491 function system_authorized_get_url(array $options = []) {
492   // core/authorize.php is an unrouted URL, so using the base: scheme is
493   // the correct usage for this case.
494   $url = Url::fromUri('base:core/authorize.php');
495   $url_options = $url->getOptions();
496   $url->setOptions($options + $url_options);
497   return $url;
498 }
499
500 /**
501  * Returns the URL for the authorize.php script when it is processing a batch.
502  *
503  * @param array $options
504  *   Optional array of options to set on the \Drupal\Core\Url object.
505  *
506  * @return \Drupal\Core\Url
507  */
508 function system_authorized_batch_processing_url(array $options = []) {
509   $options['query'] = ['batch' => '1'];
510   return system_authorized_get_url($options);
511 }
512
513 /**
514  * Setup and invoke an operation using authorize.php.
515  *
516  * @see system_authorized_init()
517  */
518 function system_authorized_run($callback, $file, $arguments = [], $page_title = NULL) {
519   system_authorized_init($callback, $file, $arguments, $page_title);
520   return new RedirectResponse(system_authorized_get_url()->toString());
521 }
522
523 /**
524  * Use authorize.php to run batch_process().
525  *
526  * @see batch_process()
527  */
528 function system_authorized_batch_process() {
529   $finish_url = system_authorized_get_url();
530   $process_url = system_authorized_batch_processing_url();
531   return batch_process($finish_url->setAbsolute()->toString(), $process_url);
532 }
533
534 /**
535  * @} End of "defgroup authorize".
536  */
537
538 /**
539  * Implements hook_updater_info().
540  */
541 function system_updater_info() {
542   return [
543     'module' => [
544       'class' => 'Drupal\Core\Updater\Module',
545       'name' => t('Update modules'),
546       'weight' => 0,
547     ],
548     'theme' => [
549       'class' => 'Drupal\Core\Updater\Theme',
550       'name' => t('Update themes'),
551       'weight' => 0,
552     ],
553   ];
554 }
555
556 /**
557  * Implements hook_filetransfer_info().
558  */
559 function system_filetransfer_info() {
560   $backends = [];
561
562   // This is the default, will be available on most systems.
563   if (function_exists('ftp_connect')) {
564     $backends['ftp'] = [
565       'title' => t('FTP'),
566       'class' => 'Drupal\Core\FileTransfer\FTP',
567       'weight' => 0,
568     ];
569   }
570
571   // SSH2 lib connection is only available if the proper PHP extension is
572   // installed.
573   if (function_exists('ssh2_connect')) {
574     $backends['ssh'] = [
575       'title' => t('SSH'),
576       'class' => 'Drupal\Core\FileTransfer\SSH',
577       'weight' => 20,
578     ];
579   }
580   return $backends;
581 }
582
583 /**
584  * Implements hook_page_attachments().
585  *
586  * @see template_preprocess_maintenance_page()
587  * @see \Drupal\Core\EventSubscriber\ActiveLinkResponseFilter
588  */
589 function system_page_attachments(array &$page) {
590   // Ensure the same CSS is loaded in template_preprocess_maintenance_page().
591   $page['#attached']['library'][] = 'system/base';
592   if (\Drupal::service('router.admin_context')->isAdminRoute()) {
593     $page['#attached']['library'][] = 'system/admin';
594   }
595
596   // Attach libraries used by this theme.
597   $active_theme = \Drupal::theme()->getActiveTheme();
598   foreach ($active_theme->getLibraries() as $library) {
599     $page['#attached']['library'][] = $library;
600   }
601
602   // Attach favicon.
603   if (theme_get_setting('features.favicon')) {
604     $favicon = theme_get_setting('favicon.url');
605     $type = theme_get_setting('favicon.mimetype');
606     $page['#attached']['html_head_link'][][] = [
607       'rel' => 'shortcut icon',
608       'href' => UrlHelper::stripDangerousProtocols($favicon),
609       'type' => $type,
610     ];
611   }
612
613   // Get the major Drupal version.
614   list($version,) = explode('.', \Drupal::VERSION);
615
616   // Attach default meta tags.
617   $meta_default = [
618     // Make sure the Content-Type comes first because the IE browser may be
619     // vulnerable to XSS via encoding attacks from any content that comes
620     // before this META tag, such as a TITLE tag.
621     'system_meta_content_type' => [
622       '#tag' => 'meta',
623       '#attributes' => [
624         'charset' => 'utf-8',
625       ],
626       // Security: This always has to be output first.
627       '#weight' => -1000,
628     ],
629     // Show Drupal and the major version number in the META GENERATOR tag.
630     'system_meta_generator' => [
631       '#type' => 'html_tag',
632       '#tag' => 'meta',
633       '#attributes' => [
634         'name' => 'Generator',
635         'content' => 'Drupal ' . $version . ' (https://www.drupal.org)',
636       ],
637     ],
638     // Attach default mobile meta tags for responsive design.
639     'MobileOptimized' => [
640       '#tag' => 'meta',
641       '#attributes' => [
642         'name' => 'MobileOptimized',
643         'content' => 'width',
644       ],
645     ],
646     'HandheldFriendly' => [
647       '#tag' => 'meta',
648       '#attributes' => [
649         'name' => 'HandheldFriendly',
650         'content' => 'true',
651       ],
652     ],
653     'viewport' => [
654       '#tag' => 'meta',
655       '#attributes' => [
656         'name' => 'viewport',
657         'content' => 'width=device-width, initial-scale=1.0',
658       ],
659     ],
660   ];
661   foreach ($meta_default as $key => $value) {
662     $page['#attached']['html_head'][] = [$value, $key];
663   }
664
665   // Handle setting the "active" class on links by:
666   // - loading the active-link library if the current user is authenticated;
667   // - applying a response filter if the current user is anonymous.
668   // @see \Drupal\Core\Link
669   // @see \Drupal\Core\Utility\LinkGenerator::generate()
670   // @see template_preprocess_links()
671   // @see \Drupal\Core\EventSubscriber\ActiveLinkResponseFilter
672   if (\Drupal::currentUser()->isAuthenticated()) {
673     $page['#attached']['library'][] = 'core/drupal.active-link';
674   }
675 }
676
677 /**
678  * Implements hook_js_settings_build().
679  *
680  * Sets values for the core/drupal.ajax library, which just depends on the
681  * active theme but no other request-dependent values.
682  */
683 function system_js_settings_build(&$settings, AttachedAssetsInterface $assets) {
684   // Generate the values for the core/drupal.ajax library.
685   // We need to send ajaxPageState settings for core/drupal.ajax if:
686   // - ajaxPageState is being loaded in this Response, in which case it will
687   //   already exist at $settings['ajaxPageState'] (because the core/drupal.ajax
688   //   library definition specifies a placeholder 'ajaxPageState' setting).
689   // - core/drupal.ajax already has been loaded and hence this is an AJAX
690   //   Response in which we must send the list of extra asset libraries that are
691   //   being added in this AJAX Response.
692   /** @var \Drupal\Core\Asset\LibraryDependencyResolver $library_dependency_resolver */
693   $library_dependency_resolver = \Drupal::service('library.dependency_resolver');
694   if (isset($settings['ajaxPageState']) || in_array('core/drupal.ajax', $library_dependency_resolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()))) {
695     // Provide the page with information about the theme that's used, so that
696     // a later AJAX request can be rendered using the same theme.
697     // @see \Drupal\Core\Theme\AjaxBasePageNegotiator
698     $theme_key = \Drupal::theme()->getActiveTheme()->getName();
699     $settings['ajaxPageState']['theme'] = $theme_key;
700   }
701 }
702
703 /**
704  * Implements hook_js_settings_alter().
705  *
706  * Sets values which depend on the current request, like core/drupalSettings
707  * as well as theme_token ajax state.
708  */
709 function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) {
710   // As this is being output in the final response always use the master
711   // request.
712   $request = \Drupal::requestStack()->getMasterRequest();
713   $current_query = $request->query->all();
714
715   // Let output path processors set a prefix.
716   /** @var \Drupal\Core\PathProcessor\OutboundPathProcessorInterface $path_processor */
717   $path_processor = \Drupal::service('path_processor_manager');
718   $options = ['prefix' => ''];
719   $path_processor->processOutbound('/', $options);
720   $pathPrefix = $options['prefix'];
721
722   $route_match = \Drupal::routeMatch();
723   if ($route_match instanceof StackedRouteMatchInterface) {
724     $route_match = $route_match->getMasterRouteMatch();
725   }
726   $current_path = $route_match->getRouteName() ? Url::fromRouteMatch($route_match)->getInternalPath() : '';
727   $current_path_is_admin = \Drupal::service('router.admin_context')->isAdminRoute($route_match->getRouteObject());
728   $path_settings = [
729     'baseUrl' => $request->getBaseUrl() . '/',
730     'pathPrefix' => $pathPrefix,
731     'currentPath' => $current_path,
732     'currentPathIsAdmin' => $current_path_is_admin,
733     'isFront' => \Drupal::service('path.matcher')->isFrontPage(),
734     'currentLanguage' => \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_URL)->getId(),
735   ];
736   if (!empty($current_query)) {
737     ksort($current_query);
738     $path_settings['currentQuery'] = (object) $current_query;
739   }
740
741   // Only set core/drupalSettings values that haven't been set already.
742   foreach ($path_settings as $key => $value) {
743     if (!isset($settings['path'][$key])) {
744       $settings['path'][$key] = $value;
745     }
746   }
747   if (!isset($settings['pluralDelimiter'])) {
748     $settings['pluralDelimiter'] = LOCALE_PLURAL_DELIMITER;
749   }
750   // Add the theme token to ajaxPageState, ensuring the database is available
751   // before doing so. Also add the loaded libraries to ajaxPageState.
752   /** @var \Drupal\Core\Asset\LibraryDependencyResolver $library_dependency_resolver */
753   $library_dependency_resolver = \Drupal::service('library.dependency_resolver');
754   if (isset($settings['ajaxPageState']) || in_array('core/drupal.ajax', $library_dependency_resolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()))) {
755     if (!defined('MAINTENANCE_MODE')) {
756       // The theme token is only validated when the theme requested is not the
757       // default, so don't generate it unless necessary.
758       // @see \Drupal\Core\Theme\AjaxBasePageNegotiator::determineActiveTheme()
759       $active_theme_key = \Drupal::theme()->getActiveTheme()->getName();
760       if ($active_theme_key !== \Drupal::service('theme_handler')->getDefault()) {
761         $settings['ajaxPageState']['theme_token'] = \Drupal::csrfToken()
762           ->get($active_theme_key);
763       }
764     }
765     // Provide the page with information about the individual asset libraries
766     // used, information not otherwise available when aggregation is enabled.
767     $minimal_libraries = $library_dependency_resolver->getMinimalRepresentativeSubset(array_merge(
768       $assets->getLibraries(),
769       $assets->getAlreadyLoadedLibraries()
770     ));
771     sort($minimal_libraries);
772     $settings['ajaxPageState']['libraries'] = implode(',', $minimal_libraries);
773   }
774 }
775
776 /**
777  * Implements hook_form_alter().
778  */
779 function system_form_alter(&$form, FormStateInterface $form_state) {
780   // If the page that's being built is cacheable, set the 'immutable' flag, to
781   // ensure that when the form is used, a new form build ID is generated when
782   // appropriate, to prevent information disclosure.
783
784   // Note: This code just wants to know whether cache response headers are set,
785   // not whether page_cache module will be active.
786   // \Drupal\Core\EventSubscriber\FinishResponseSubscriber::onRespond will
787   // send those headers, in case $request_policy->check($request) succeeds. In
788   // that case we need to ensure that the immutable flag is sot, so future POST
789   // request won't take over the form state of another user.
790   /** @var \Drupal\Core\PageCache\RequestPolicyInterface $request_policy */
791   $request_policy = \Drupal::service('page_cache_request_policy');
792   $request = \Drupal::requestStack()->getCurrentRequest();
793   $request_is_cacheable = $request_policy->check($request) === RequestPolicyInterface::ALLOW;
794   if ($request_is_cacheable) {
795     $form_state->addBuildInfo('immutable', TRUE);
796   }
797 }
798
799 /**
800  * Implements hook_form_FORM_ID_alter() for \Drupal\user\AccountForm.
801  */
802 function system_form_user_form_alter(&$form, FormStateInterface $form_state) {
803   if (\Drupal::config('system.date')->get('timezone.user.configurable')) {
804     system_user_timezone($form, $form_state);
805   }
806 }
807
808 /**
809  * Implements hook_form_FORM_ID_alter() for \Drupal\user\RegisterForm.
810  */
811 function system_form_user_register_form_alter(&$form, FormStateInterface $form_state) {
812   $config = \Drupal::config('system.date');
813   if ($config->get('timezone.user.configurable') && $config->get('timezone.user.default') == DRUPAL_USER_TIMEZONE_SELECT) {
814     system_user_timezone($form, $form_state);
815   }
816 }
817
818 /**
819  * Implements hook_ENTITY_TYPE_presave() for user entities.
820  */
821 function system_user_presave(UserInterface $account) {
822   $config = \Drupal::config('system.date');
823   if ($config->get('timezone.user.configurable') && !$account->getTimeZone() && !$config->get('timezone.user.default')) {
824     $account->timezone = $config->get('timezone.default');
825   }
826 }
827
828 /**
829  * Implements hook_user_login().
830  */
831 function system_user_login(UserInterface $account) {
832   $config = \Drupal::config('system.date');
833   // If the user has a NULL time zone, notify them to set a time zone.
834   if (!$account->getTimezone() && $config->get('timezone.user.configurable') && $config->get('timezone.user.warn')) {
835     drupal_set_message(t('Configure your <a href=":user-edit">account time zone setting</a>.', [':user-edit' => $account->url('edit-form', ['query' => \Drupal::destination()->getAsArray(), 'fragment' => 'edit-timezone'])]));
836   }
837 }
838
839 /**
840  * Add the time zone field to the user edit and register forms.
841  */
842 function system_user_timezone(&$form, FormStateInterface $form_state) {
843   $user = \Drupal::currentUser();
844
845   $account = $form_state->getFormObject()->getEntity();
846   $form['timezone'] = [
847     '#type' => 'details',
848     '#title' => t('Locale settings'),
849     '#open' => TRUE,
850     '#weight' => 6,
851   ];
852   $form['timezone']['timezone'] = [
853     '#type' => 'select',
854     '#title' => t('Time zone'),
855     '#default_value' => $account->getTimezone() ? $account->getTimezone() : \Drupal::config('system.date')->get('timezone.default'),
856     '#options' => system_time_zones($account->id() != $user->id(), TRUE),
857     '#description' => t('Select the desired local time and time zone. Dates and times throughout this site will be displayed using this time zone.'),
858   ];
859   $user_input = $form_state->getUserInput();
860   if (!$account->getTimezone() && $account->id() == $user->id() && empty($user_input['timezone'])) {
861     $form['timezone']['#attached']['library'][] = 'core/drupal.timezone';
862     $form['timezone']['timezone']['#attributes'] = ['class' => ['timezone-detect']];
863   }
864 }
865
866 /**
867  * Implements hook_preprocess_HOOK() for block templates.
868  */
869 function system_preprocess_block(&$variables) {
870   switch ($variables['base_plugin_id']) {
871     case 'system_branding_block':
872       $variables['site_logo'] = '';
873       if ($variables['content']['site_logo']['#access'] && $variables['content']['site_logo']['#uri']) {
874         $variables['site_logo'] = $variables['content']['site_logo']['#uri'];
875       }
876       $variables['site_name'] = '';
877       if ($variables['content']['site_name']['#access'] && $variables['content']['site_name']['#markup']) {
878         $variables['site_name'] = $variables['content']['site_name']['#markup'];
879       }
880       $variables['site_slogan'] = '';
881       if ($variables['content']['site_slogan']['#access'] && $variables['content']['site_slogan']['#markup']) {
882         $variables['site_slogan'] = [
883           '#markup' => $variables['content']['site_slogan']['#markup'],
884         ];
885       }
886       break;
887
888     case 'system_powered_by_block':
889       $variables['attributes']['role'] = 'complementary';
890       break;
891   }
892 }
893
894 /**
895  * Checks the existence of the directory specified in $form_element.
896  *
897  * This function is called from the system_settings form to check all core
898  * file directories (file_public_path, file_private_path, file_temporary_path).
899  *
900  * @param $form_element
901  *   The form element containing the name of the directory to check.
902  * @param \Drupal\Core\Form\FormStateInterface $form_state
903  *   The current state of the form.
904  */
905 function system_check_directory($form_element, FormStateInterface $form_state) {
906   $directory = $form_element['#value'];
907   if (strlen($directory) == 0) {
908     return $form_element;
909   }
910
911   $logger = \Drupal::logger('file system');
912   if (!is_dir($directory) && !drupal_mkdir($directory, NULL, TRUE)) {
913     // If the directory does not exists and cannot be created.
914     $form_state->setErrorByName($form_element['#parents'][0], t('The directory %directory does not exist and could not be created.', ['%directory' => $directory]));
915     $logger->error('The directory %directory does not exist and could not be created.', ['%directory' => $directory]);
916   }
917
918   if (is_dir($directory) && !is_writable($directory) && !drupal_chmod($directory)) {
919     // If the directory is not writable and cannot be made so.
920     $form_state->setErrorByName($form_element['#parents'][0], t('The directory %directory exists but is not writable and could not be made writable.', ['%directory' => $directory]));
921     $logger->error('The directory %directory exists but is not writable and could not be made writable.', ['%directory' => $directory]);
922   }
923   elseif (is_dir($directory)) {
924     if ($form_element['#name'] == 'file_public_path') {
925       // Create public .htaccess file.
926       file_save_htaccess($directory, FALSE);
927     }
928     else {
929       // Create private .htaccess file.
930       file_save_htaccess($directory);
931     }
932   }
933
934   return $form_element;
935 }
936
937 /**
938  * Returns an array of information about enabled modules or themes.
939  *
940  * This function returns the contents of the .info.yml file for each installed
941  * module or theme.
942  *
943  * @param $type
944  *   Either 'module' or 'theme'.
945  * @param $name
946  *   (optional) The name of a module or theme whose information shall be
947  *   returned. If omitted, all records for the provided $type will be returned.
948  *   If $name does not exist in the provided $type or is not enabled, an empty
949  *   array will be returned.
950  *
951  * @return
952  *   An associative array of module or theme information keyed by name, or only
953  *   information for $name, if given. If no records are available, an empty
954  *   array is returned.
955  *
956  * @see system_rebuild_module_data()
957  * @see \Drupal\Core\Extension\ThemeHandlerInterface::rebuildThemeData()
958  */
959 function system_get_info($type, $name = NULL) {
960   if ($type == 'module') {
961     $info = &drupal_static(__FUNCTION__);
962     if (!isset($info)) {
963       if ($cache = \Drupal::cache()->get('system.module.info')) {
964         $info = $cache->data;
965       }
966       else {
967         $data = system_rebuild_module_data();
968         foreach (\Drupal::moduleHandler()->getModuleList() as $module => $filename) {
969           if (isset($data[$module])) {
970             $info[$module] = $data[$module]->info;
971           }
972         }
973         // Store the module information in cache. This cache is cleared by
974         // calling system_rebuild_module_data(), for example, when listing
975         // modules, (un)installing modules, importing configuration, updating
976         // the site and when flushing all the caches.
977         \Drupal::cache()->set('system.module.info', $info);
978       }
979     }
980   }
981   else {
982     $info = [];
983     $list = system_list($type);
984     foreach ($list as $shortname => $item) {
985       if (!empty($item->status)) {
986         $info[$shortname] = $item->info;
987       }
988     }
989   }
990   if (isset($name)) {
991     return isset($info[$name]) ? $info[$name] : [];
992   }
993   return $info;
994 }
995
996 /**
997  * Helper function to scan and collect module .info.yml data.
998  *
999  * @return \Drupal\Core\Extension\Extension[]
1000  *   An associative array of module information.
1001  */
1002 function _system_rebuild_module_data() {
1003   $listing = new ExtensionDiscovery(\Drupal::root());
1004
1005   // Find installation profiles. This needs to happen before performing a
1006   // module scan as the module scan requires knowing what the active profile is.
1007   // @todo Remove as part of https://www.drupal.org/node/2186491.
1008   $profiles = $listing->scan('profile');
1009   $profile = drupal_get_profile();
1010   if ($profile && isset($profiles[$profile])) {
1011     // Prime the drupal_get_filename() static cache with the profile info file
1012     // location so we can use drupal_get_path() on the active profile during
1013     // the module scan.
1014     // @todo Remove as part of https://www.drupal.org/node/2186491.
1015     drupal_get_filename('profile', $profile, $profiles[$profile]->getPathname());
1016   }
1017
1018   // Find modules.
1019   $modules = $listing->scan('module');
1020   // Include the installation profile in modules that are loaded.
1021   if ($profile) {
1022     $modules[$profile] = $profiles[$profile];
1023     // Installation profile hooks are always executed last.
1024     $modules[$profile]->weight = 1000;
1025   }
1026
1027   // Set defaults for module info.
1028   $defaults = [
1029     'dependencies' => [],
1030     'description' => '',
1031     'package' => 'Other',
1032     'version' => NULL,
1033     'php' => DRUPAL_MINIMUM_PHP,
1034   ];
1035
1036   // Read info files for each module.
1037   foreach ($modules as $key => $module) {
1038     // Look for the info file.
1039     $module->info = \Drupal::service('info_parser')->parse($module->getPathname());
1040
1041     // Add the info file modification time, so it becomes available for
1042     // contributed modules to use for ordering module lists.
1043     $module->info['mtime'] = $module->getMTime();
1044
1045     // Merge in defaults and save.
1046     $modules[$key]->info = $module->info + $defaults;
1047
1048     // Installation profiles are hidden by default, unless explicitly specified
1049     // otherwise in the .info.yml file.
1050     if ($key == $profile && !isset($modules[$key]->info['hidden'])) {
1051       $modules[$key]->info['hidden'] = TRUE;
1052     }
1053
1054     // Invoke hook_system_info_alter() to give installed modules a chance to
1055     // modify the data in the .info.yml files if necessary.
1056     // @todo Remove $type argument, obsolete with $module->getType().
1057     $type = 'module';
1058     \Drupal::moduleHandler()->alter('system_info', $modules[$key]->info, $modules[$key], $type);
1059   }
1060
1061   // It is possible that a module was marked as required by
1062   // hook_system_info_alter() and modules that it depends on are not required.
1063   foreach ($modules as $module) {
1064     _system_rebuild_module_data_ensure_required($module, $modules);
1065   }
1066
1067   if ($profile && isset($modules[$profile])) {
1068     // The installation profile is required, if it's a valid module.
1069     $modules[$profile]->info['required'] = TRUE;
1070     // Add a default distribution name if the profile did not provide one.
1071     // @see install_profile_info()
1072     // @see drupal_install_profile_distribution_name()
1073     if (!isset($modules[$profile]->info['distribution']['name'])) {
1074       $modules[$profile]->info['distribution']['name'] = 'Drupal';
1075     }
1076   }
1077
1078   return $modules;
1079 }
1080
1081 /**
1082  * Ensures that dependencies of required modules are also required.
1083  *
1084  * @param \Drupal\Core\Extension\Extension $module
1085  *   The module info.
1086  * @param \Drupal\Core\Extension\Extension[] $modules
1087  *   The array of all module info.
1088  */
1089 function _system_rebuild_module_data_ensure_required($module, &$modules) {
1090   if (!empty($module->info['required'])) {
1091     foreach ($module->info['dependencies'] as $dependency) {
1092       $dependency_name = ModuleHandler::parseDependency($dependency)['name'];
1093       if (!isset($modules[$dependency_name]->info['required'])) {
1094         $modules[$dependency_name]->info['required'] = TRUE;
1095         $modules[$dependency_name]->info['explanation'] = t('Dependency of required module @module', ['@module' => $module->info['name']]);
1096         // Ensure any dependencies it has are required.
1097         _system_rebuild_module_data_ensure_required($modules[$dependency_name], $modules);
1098       }
1099     }
1100   }
1101 }
1102
1103 /**
1104  * Rebuild, save, and return data about all currently available modules.
1105  *
1106  * @return \Drupal\Core\Extension\Extension[]
1107  *   Array of all available modules and their data.
1108  */
1109 function system_rebuild_module_data() {
1110   $modules_cache = &drupal_static(__FUNCTION__);
1111   // Only rebuild once per request. $modules and $modules_cache cannot be
1112   // combined into one variable, because the $modules_cache variable is reset by
1113   // reference from system_list_reset() during the rebuild.
1114   if (!isset($modules_cache)) {
1115     $modules = _system_rebuild_module_data();
1116     $files = [];
1117     ksort($modules);
1118     // Add status, weight, and schema version.
1119     $installed_modules = \Drupal::config('core.extension')->get('module') ?: [];
1120     foreach ($modules as $name => $module) {
1121       $module->weight = isset($installed_modules[$name]) ? $installed_modules[$name] : 0;
1122       $module->status = (int) isset($installed_modules[$name]);
1123       $module->schema_version = SCHEMA_UNINSTALLED;
1124       $files[$name] = $module->getPathname();
1125     }
1126     $modules = \Drupal::moduleHandler()->buildModuleDependencies($modules);
1127     $modules_cache = $modules;
1128
1129     // Store filenames to allow drupal_get_filename() to retrieve them without
1130     // having to rebuild or scan the filesystem.
1131     \Drupal::state()->set('system.module.files', $files);
1132     // Clear the module info cache.
1133     \Drupal::cache()->delete('system.module.info');
1134     drupal_static_reset('system_get_info');
1135   }
1136   return $modules_cache;
1137 }
1138
1139 /**
1140  * Get a list of available regions from a specified theme.
1141  *
1142  * @param \Drupal\Core\Extension\Extension|string $theme
1143  *   A theme extension object, or the name of a theme.
1144  * @param $show
1145  *   Possible values: REGIONS_ALL or REGIONS_VISIBLE. Visible excludes hidden
1146  *   regions.
1147  * @return
1148  *   An array of regions in the form $region['name'] = 'description'.
1149  */
1150 function system_region_list($theme, $show = REGIONS_ALL) {
1151   if (!$theme instanceof Extension) {
1152     $themes = \Drupal::service('theme_handler')->listInfo();
1153     if (!isset($themes[$theme])) {
1154       return [];
1155     }
1156     $theme = $themes[$theme];
1157   }
1158   $list = [];
1159   $info = $theme->info;
1160   // If requested, suppress hidden regions. See block_admin_display_form().
1161   foreach ($info['regions'] as $name => $label) {
1162     if ($show == REGIONS_ALL || !isset($info['regions_hidden']) || !in_array($name, $info['regions_hidden'])) {
1163       $list[$name] = t($label);
1164     }
1165   }
1166
1167   return $list;
1168 }
1169
1170 /**
1171  * Array sorting callback; sorts modules by their name.
1172  */
1173 function system_sort_modules_by_info_name($a, $b) {
1174   return strcasecmp($a->info['name'], $b->info['name']);
1175 }
1176
1177 /**
1178  * Sorts themes by their names, with the default theme listed first.
1179  *
1180  * Callback for uasort() within
1181  * \Drupal\system\Controller\SystemController::themesPage().
1182  *
1183  * @see system_sort_modules_by_info_name()
1184  */
1185 function system_sort_themes($a, $b) {
1186   if ($a->is_default) {
1187     return -1;
1188   }
1189   if ($b->is_default) {
1190     return 1;
1191   }
1192   return strcasecmp($a->info['name'], $b->info['name']);
1193 }
1194
1195 /**
1196  * Implements hook_system_info_alter().
1197  */
1198 function system_system_info_alter(&$info, Extension $file, $type) {
1199   // Remove page-top and page-bottom from the blocks UI since they are reserved for
1200   // modules to populate from outside the blocks system.
1201   if ($type == 'theme') {
1202     $info['regions_hidden'][] = 'page_top';
1203     $info['regions_hidden'][] = 'page_bottom';
1204   }
1205 }
1206
1207 /**
1208  * Gets the name of the default region for a given theme.
1209  *
1210  * @param $theme
1211  *   The name of a theme.
1212  * @return
1213  *   A string that is the region name.
1214  */
1215 function system_default_region($theme) {
1216   $regions = array_keys(system_region_list($theme, REGIONS_VISIBLE));
1217   return isset($regions[0]) ? $regions[0] : '';
1218 }
1219
1220 /**
1221  * Determines whether the current user is in compact mode.
1222  *
1223  * Compact mode shows certain administration pages with less description text,
1224  * such as the configuration page and the permissions page.
1225  *
1226  * Whether the user is in compact mode is determined by a cookie, which is set
1227  * for the user by \Drupal\system\Controller\SystemController::compactPage().
1228  *
1229  * If the user does not have the cookie, the default value is given by the
1230  * configuration variable 'system.site.admin_compact_mode', which itself
1231  * defaults to FALSE. This does not have a user interface to set it: it is a
1232  * hidden variable which can be set in the settings.php file.
1233  *
1234  * @return bool
1235  *   TRUE when in compact mode, FALSE when in expanded mode.
1236  */
1237 function system_admin_compact_mode() {
1238   // PHP converts dots into underscores in cookie names to avoid problems with
1239   // its parser, so we use a converted cookie name.
1240   return \Drupal::request()->cookies->get('Drupal_visitor_admin_compact_mode', \Drupal::config('system.site')->get('admin_compact_mode'));
1241 }
1242
1243 /**
1244  * Generate a list of tasks offered by a specified module.
1245  *
1246  * @param string $module
1247  *   Module name.
1248  * @param array $info
1249  *   The module's information, as provided by system_get_info().
1250  *
1251  * @return array
1252  *   An array of task links.
1253  */
1254 function system_get_module_admin_tasks($module, array $info) {
1255   $tree = &drupal_static(__FUNCTION__);
1256
1257   $menu_tree = \Drupal::menuTree();
1258
1259   if (!isset($tree)) {
1260     $parameters = new MenuTreeParameters();
1261     $parameters->setRoot('system.admin')->excludeRoot()->onlyEnabledLinks();
1262     $tree = $menu_tree->load('system.admin', $parameters);
1263     $manipulators = [
1264       ['callable' => 'menu.default_tree_manipulators:checkAccess'],
1265       ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
1266       ['callable' => 'menu.default_tree_manipulators:flatten'],
1267     ];
1268     $tree = $menu_tree->transform($tree, $manipulators);
1269   }
1270
1271   $admin_tasks = [];
1272   foreach ($tree as $element) {
1273     if (!$element->access->isAllowed()) {
1274       // @todo Bubble cacheability metadata of both accessible and inaccessible
1275       //   links. Currently made impossible by the way admin tasks are rendered.
1276       continue;
1277     }
1278
1279     $link = $element->link;
1280     if ($link->getProvider() != $module) {
1281       continue;
1282     }
1283     $admin_tasks[] = [
1284       'title' => $link->getTitle(),
1285       'description' => $link->getDescription(),
1286       'url' => $link->getUrlObject(),
1287     ];
1288   }
1289
1290   // Append link for permissions.
1291   /** @var \Drupal\user\PermissionHandlerInterface $permission_handler */
1292   $permission_handler = \Drupal::service('user.permissions');
1293
1294   if ($permission_handler->moduleProvidesPermissions($module)) {
1295     /** @var \Drupal\Core\Access\AccessManagerInterface $access_manager */
1296     $access_manager = \Drupal::service('access_manager');
1297     if ($access_manager->checkNamedRoute('user.admin_permissions', [], \Drupal::currentUser())) {
1298       /** @var \Drupal\Core\Url $url */
1299       $url = new Url('user.admin_permissions');
1300       $url->setOption('fragment', 'module-' . $module);
1301       $admin_tasks["user.admin_permissions.$module"] = [
1302         'title' => t('Configure @module permissions', ['@module' => $info['name']]),
1303         'description' => '',
1304         'url' => $url,
1305       ];
1306     }
1307   }
1308
1309   return $admin_tasks;
1310 }
1311
1312 /**
1313  * Implements hook_cron().
1314  *
1315  * Remove older rows from flood, batch cache and expirable keyvalue tables.
1316  */
1317 function system_cron() {
1318   // Clean up the flood.
1319   \Drupal::flood()->garbageCollection();
1320
1321   foreach (Cache::getBins() as $cache_backend) {
1322     $cache_backend->garbageCollection();
1323   }
1324
1325   // Clean up the expirable key value database store.
1326   if (\Drupal::service('keyvalue.expirable.database') instanceof KeyValueDatabaseExpirableFactory) {
1327     \Drupal::service('keyvalue.expirable.database')->garbageCollection();
1328   }
1329
1330   // Clean up any garbage in the queue service.
1331   $queue_worker_manager = \Drupal::service('plugin.manager.queue_worker');
1332   $queue_factory = \Drupal::service('queue');
1333
1334   foreach (array_keys($queue_worker_manager->getDefinitions()) as $queue_name) {
1335     $queue = $queue_factory->get($queue_name);
1336
1337     if ($queue instanceof QueueGarbageCollectionInterface) {
1338       $queue->garbageCollection();
1339     }
1340   }
1341
1342   // Clean up PHP storage.
1343   PhpStorageFactory::get('container')->garbageCollection();
1344   PhpStorageFactory::get('service_container')->garbageCollection();
1345 }
1346
1347 /**
1348  * Implements hook_mail().
1349  */
1350 function system_mail($key, &$message, $params) {
1351   $token_service = \Drupal::token();
1352
1353   $context = $params['context'];
1354
1355   $subject = PlainTextOutput::renderFromHtml($token_service->replace($context['subject'], $context));
1356   $body = $token_service->replace($context['message'], $context);
1357
1358   $message['subject'] .= str_replace(["\r", "\n"], '', $subject);
1359   $message['body'][] = $body;
1360 }
1361
1362 /**
1363  * Generate an array of time zones and their local time&date.
1364  *
1365  * @param mixed $blank
1366  *   If evaluates true, prepend an empty time zone option to the array.
1367  * @param bool $grouped
1368  *   (optional) Whether the timezones should be grouped by region.
1369  *
1370  * @return array
1371  *   An array or nested array containing time zones, keyed by the system name.
1372  */
1373 function system_time_zones($blank = NULL, $grouped = FALSE) {
1374   $zonelist = timezone_identifiers_list();
1375   $zones = $blank ? ['' => t('- None selected -')] : [];
1376   foreach ($zonelist as $zone) {
1377     // Because many time zones exist in PHP only for backward compatibility
1378     // reasons and should not be used, the list is filtered by a regular
1379     // expression.
1380     if (preg_match('!^((Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific)/|UTC$)!', $zone)) {
1381       $zones[$zone] = t('@zone', ['@zone' => t(str_replace('_', ' ', $zone))]);
1382     }
1383   }
1384   // Sort the translated time zones alphabetically.
1385   asort($zones);
1386   if ($grouped) {
1387     $grouped_zones = [];
1388     foreach ($zones as $key => $value) {
1389       $split = explode('/', $value);
1390       $city = array_pop($split);
1391       $region = array_shift($split);
1392       if (!empty($region)) {
1393         $grouped_zones[$region][$key] = empty($split) ? $city : $city . ' (' . implode('/', $split) . ')';
1394       }
1395       else {
1396         $grouped_zones[$key] = $value;
1397       }
1398     }
1399     foreach ($grouped_zones as $key => $value) {
1400       if (is_array($grouped_zones[$key])) {
1401         asort($grouped_zones[$key]);
1402       }
1403     }
1404     $zones = $grouped_zones;
1405   }
1406
1407   return $zones;
1408 }
1409
1410 /**
1411  * Attempts to get a file using Guzzle HTTP client and to store it locally.
1412  *
1413  * @param string $url
1414  *   The URL of the file to grab.
1415  * @param string $destination
1416  *   Stream wrapper URI specifying where the file should be placed. If a
1417  *   directory path is provided, the file is saved into that directory under
1418  *   its original name. If the path contains a filename as well, that one will
1419  *   be used instead.
1420  *   If this value is omitted, the site's default files scheme will be used,
1421  *   usually "public://".
1422  * @param bool $managed
1423  *   If this is set to TRUE, the file API hooks will be invoked and the file is
1424  *   registered in the database.
1425  * @param int $replace
1426  *   Replace behavior when the destination file already exists:
1427  *   - FILE_EXISTS_REPLACE: Replace the existing file.
1428  *   - FILE_EXISTS_RENAME: Append _{incrementing number} until the filename is
1429  *     unique.
1430  *   - FILE_EXISTS_ERROR: Do nothing and return FALSE.
1431  *
1432  * @return mixed
1433  *   One of these possibilities:
1434  *   - If it succeeds and $managed is FALSE, the location where the file was
1435  *     saved.
1436  *   - If it succeeds and $managed is TRUE, a \Drupal\file\FileInterface
1437  *     object which describes the file.
1438  *   - If it fails, FALSE.
1439  */
1440 function system_retrieve_file($url, $destination = NULL, $managed = FALSE, $replace = FILE_EXISTS_RENAME) {
1441   $parsed_url = parse_url($url);
1442   if (!isset($destination)) {
1443     $path = file_build_uri(drupal_basename($parsed_url['path']));
1444   }
1445   else {
1446     if (is_dir(drupal_realpath($destination))) {
1447       // Prevent URIs with triple slashes when glueing parts together.
1448       $path = str_replace('///', '//', "$destination/") . drupal_basename($parsed_url['path']);
1449     }
1450     else {
1451       $path = $destination;
1452     }
1453   }
1454   try {
1455     $data = (string) \Drupal::httpClient()
1456       ->get($url)
1457       ->getBody();
1458     $local = $managed ? file_save_data($data, $path, $replace) : file_unmanaged_save_data($data, $path, $replace);
1459   }
1460   catch (RequestException $exception) {
1461     drupal_set_message(t('Failed to fetch file due to error "%error"', ['%error' => $exception->getMessage()]), 'error');
1462     return FALSE;
1463   }
1464   if (!$local) {
1465     drupal_set_message(t('@remote could not be saved to @path.', ['@remote' => $url, '@path' => $path]), 'error');
1466   }
1467
1468   return $local;
1469 }
1470
1471 /**
1472  * Implements hook_entity_type_build().
1473  */
1474 function system_entity_type_build(array &$entity_types) {
1475   /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
1476   $entity_types['date_format']
1477     ->setFormClass('add', 'Drupal\system\Form\DateFormatAddForm')
1478     ->setFormClass('edit', 'Drupal\system\Form\DateFormatEditForm')
1479     ->setFormClass('delete', 'Drupal\system\Form\DateFormatDeleteForm')
1480     ->setListBuilderClass('Drupal\system\DateFormatListBuilder')
1481     ->setLinkTemplate('edit-form', '/admin/config/regional/date-time/formats/manage/{date_format}')
1482     ->setLinkTemplate('delete-form', '/admin/config/regional/date-time/formats/manage/{date_format}/delete')
1483     ->setLinkTemplate('collection', '/admin/config/regional/date-time/formats');
1484 }
1485
1486 /**
1487  * Implements hook_block_view_BASE_BLOCK_ID_alter().
1488  */
1489 function system_block_view_system_main_block_alter(array &$build, BlockPluginInterface $block) {
1490   // Contextual links on the system_main block would basically duplicate the
1491   // tabs/local tasks, so reduce the clutter.
1492   unset($build['#contextual_links']);
1493 }
1494
1495 /**
1496  * Implements hook_path_update().
1497  */
1498 function system_path_update($path) {
1499   $alias_manager = \Drupal::service('path.alias_manager');
1500   $alias_manager->cacheClear($path['source']);
1501   $alias_manager->cacheClear($path['original']['source']);
1502 }
1503
1504 /**
1505  * Implements hook_path_insert().
1506  */
1507 function system_path_insert($path) {
1508   \Drupal::service('path.alias_manager')->cacheClear($path['source']);
1509 }
1510
1511 /**
1512  * Implements hook_path_delete().
1513  */
1514 function system_path_delete($path) {
1515   \Drupal::service('path.alias_manager')->cacheClear($path['source']);
1516 }
1517
1518 /**
1519  * Implements hook_query_TAG_alter() for entity reference selection handlers.
1520  */
1521 function system_query_entity_reference_alter(AlterableInterface $query) {
1522   $handler = $query->getMetadata('entity_reference_selection_handler');
1523   $handler->entityQueryAlter($query);
1524 }