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