annotate core/modules/system/system.module @ 19:fa3358dc1485 tip

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