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