summaryrefslogtreecommitdiffstatshomepage
path: root/core/modules/system
diff options
context:
space:
mode:
Diffstat (limited to 'core/modules/system')
-rw-r--r--core/modules/system/config/schema/system.schema.yml16
-rw-r--r--core/modules/system/css/components/item-list.module.css19
-rw-r--r--core/modules/system/css/components/position-container.module.css8
-rw-r--r--core/modules/system/css/components/reset-appearance.module.css14
-rw-r--r--core/modules/system/css/components/system-status-report-counters.css2
-rw-r--r--core/modules/system/src/Controller/DbUpdateController.php21
-rw-r--r--core/modules/system/src/Controller/SystemController.php2
-rw-r--r--core/modules/system/src/CronController.php2
-rw-r--r--core/modules/system/src/Element/StatusReportPage.php23
-rw-r--r--core/modules/system/src/EventSubscriber/SecurityFileUploadEventSubscriber.php17
-rw-r--r--core/modules/system/src/Form/ModulesListForm.php4
-rw-r--r--core/modules/system/src/Form/ModulesUninstallForm.php13
-rw-r--r--core/modules/system/src/Form/ThemeSettingsForm.php7
-rw-r--r--core/modules/system/src/Hook/PageAttachmentsHook.php2
-rw-r--r--core/modules/system/src/Hook/SystemHooks.php12
-rw-r--r--core/modules/system/src/Hook/SystemRequirementsHooks.php29
-rw-r--r--core/modules/system/src/Hook/SystemThemeHooks.php138
-rw-r--r--core/modules/system/src/Install/Requirements/SystemRequirements.php1665
-rw-r--r--core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php19
-rw-r--r--core/modules/system/src/SystemManager.php39
-rw-r--r--core/modules/system/system.install1631
-rw-r--r--core/modules/system/system.libraries.yml3
-rw-r--r--core/modules/system/system.module118
-rw-r--r--core/modules/system/templates/details.html.twig6
-rw-r--r--core/modules/system/templates/field-multiple-value-form.html.twig2
-rw-r--r--core/modules/system/templates/field.html.twig2
-rw-r--r--core/modules/system/templates/image.html.twig2
-rw-r--r--core/modules/system/templates/install-page.html.twig2
-rw-r--r--core/modules/system/templates/item-list.html.twig2
-rw-r--r--core/modules/system/templates/maintenance-page.html.twig2
-rw-r--r--core/modules/system/templates/menu-local-action.html.twig2
-rw-r--r--core/modules/system/templates/menu-local-task.html.twig2
-rw-r--r--core/modules/system/templates/pager.html.twig2
-rw-r--r--core/modules/system/templates/region.html.twig2
-rw-r--r--core/modules/system/templates/table.html.twig2
-rw-r--r--core/modules/system/tests/fixtures/update/drupal-10.3.0.bare.standard.php.gzbin162581 -> 162168 bytes
-rw-r--r--core/modules/system/tests/fixtures/update/drupal-10.3.0.filled.standard.php.gzbin596855 -> 596593 bytes
-rw-r--r--core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml7
-rw-r--r--core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php31
-rw-r--r--core/modules/system/tests/modules/batch_test/src/BatchTestCallbacks.php2
-rw-r--r--core/modules/system/tests/modules/common_test/common_test.module29
-rw-r--r--core/modules/system/tests/modules/common_test/src/Hook/CommonTestHooks.php125
-rw-r--r--core/modules/system/tests/modules/common_test/src/Hook/CommonTestThemeHooks.php165
-rw-r--r--core/modules/system/tests/modules/container_initialize/container_initialize.info.yml5
-rw-r--r--core/modules/system/tests/modules/container_initialize/container_initialize.module10
-rw-r--r--core/modules/system/tests/modules/delay_cache_tags_invalidation/src/Hook/DelayCacheTagsInvalidationHooks.php12
-rw-r--r--core/modules/system/tests/modules/element_info_test/src/Element/Deprecated.php3
-rw-r--r--core/modules/system/tests/modules/element_info_test/src/Hook/ElementInfoTestHooks.php6
-rw-r--r--core/modules/system/tests/modules/element_info_test/src/Render/Element/Details.php57
-rw-r--r--core/modules/system/tests/modules/entity_crud_hook_test/src/Hook/EntityCrudHookTestHooks.php114
-rw-r--r--core/modules/system/tests/modules/experimental_module_requirements_test/experimental_module_requirements_test.install22
-rw-r--r--core/modules/system/tests/modules/experimental_module_requirements_test/src/Install/Requirements/ExperimentalModuleRequirementsTestRequirements.php29
-rw-r--r--core/modules/system/tests/modules/form_test/src/Form/FormTestClickedButtonForm.php38
-rw-r--r--core/modules/system/tests/modules/form_test/src/Form/FormTestGroupDetailsForm.php5
-rw-r--r--core/modules/system/tests/modules/form_test/src/Form/FormTestOptionalContainerForm.php2
-rw-r--r--core/modules/system/tests/modules/form_test/src/Form/FormTestStorageForm.php4
-rw-r--r--core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php2
-rw-r--r--core/modules/system/tests/modules/form_test/src/Hook/FormTestHooks.php26
-rw-r--r--core/modules/system/tests/modules/icon_test/config/schema/icon_test.schema.yml21
-rw-r--r--core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/TestToolkit.php6
-rw-r--r--core/modules/system/tests/modules/js_displace/js_displace.module15
-rw-r--r--core/modules/system/tests/modules/js_displace/src/Hook/JsDisplaceThemeHooks.php22
-rw-r--r--core/modules/system/tests/modules/layout_test/layout_test.module15
-rw-r--r--core/modules/system/tests/modules/layout_test/src/Hook/LayoutTestThemeHooks.php22
-rw-r--r--core/modules/system/tests/modules/module_install_unmet_requirements/src/Install/Requirements/ModuleInstallUnmetRequirementsRequirements.php3
-rw-r--r--core/modules/system/tests/modules/module_runtime_requirements/src/Hook/ModuleRuntimeRequirementsHooks.php7
-rw-r--r--core/modules/system/tests/modules/module_test/module_test.file.inc18
-rw-r--r--core/modules/system/tests/modules/module_test/module_test.module69
-rw-r--r--core/modules/system/tests/modules/module_test/src/Hook/ModuleTestFileThemeHooks.php40
-rw-r--r--core/modules/system/tests/modules/module_test/src/Hook/ModuleTestThemeHooks.php85
-rw-r--r--core/modules/system/tests/modules/module_test_oop_preprocess/src/Hook/ModuleTestOopPreprocessThemeHooks.php6
-rw-r--r--core/modules/system/tests/modules/module_update_requirements/src/Hook/ModuleUpdateRequirementsHooks.php7
-rw-r--r--core/modules/system/tests/modules/olivero_test/olivero_test.module25
-rw-r--r--core/modules/system/tests/modules/olivero_test/src/Hook/OliveroTestThemeHooks.php33
-rw-r--r--core/modules/system/tests/modules/pager_test/pager_test.module42
-rw-r--r--core/modules/system/tests/modules/pager_test/src/Hook/PagerTestThemeHooks.php50
-rw-r--r--core/modules/system/tests/modules/requirements1_test/requirements1_test.install8
-rw-r--r--core/modules/system/tests/modules/requirements1_test/src/Hook/Requirements1TestHooks.php3
-rw-r--r--core/modules/system/tests/modules/router_test_directory/router_test.module27
-rw-r--r--core/modules/system/tests/modules/router_test_directory/src/Hook/RouterTestThemeHooks.php33
-rw-r--r--core/modules/system/tests/modules/sdc_test/components/my-banner/my-banner.component.yml4
-rw-r--r--core/modules/system/tests/modules/sdc_test/components/my-button/my-button.component.yml4
-rw-r--r--core/modules/system/tests/modules/sdc_test/components/my-cta/my-cta.component.yml13
-rw-r--r--core/modules/system/tests/modules/sdc_test_replacements/components/my-button/my-button.component.yml4
-rw-r--r--core/modules/system/tests/modules/session_test/session_test.routing.yml22
-rw-r--r--core/modules/system/tests/modules/session_test/src/Controller/LegacySessionTestController.php35
-rw-r--r--core/modules/system/tests/modules/session_test/src/Controller/SessionTestController.php127
-rw-r--r--core/modules/system/tests/modules/system_test/src/Controller/OptionalServiceSystemTestController.php21
-rw-r--r--core/modules/system/tests/modules/test_htmx/css/style.css3
-rw-r--r--core/modules/system/tests/modules/test_htmx/js/behavior.js14
-rw-r--r--core/modules/system/tests/modules/test_htmx/src/Controller/HtmxTestAttachmentsController.php116
-rw-r--r--core/modules/system/tests/modules/test_htmx/src/Form/HtmxTestAjaxForm.php51
-rw-r--r--core/modules/system/tests/modules/test_htmx/test_htmx.info.yml4
-rw-r--r--core/modules/system/tests/modules/test_htmx/test_htmx.libraries.yml10
-rw-r--r--core/modules/system/tests/modules/test_htmx/test_htmx.routing.yml39
-rw-r--r--core/modules/system/tests/modules/theme_region_test/src/Hook/ThemeRegionTestThemeHooks.php24
-rw-r--r--core/modules/system/tests/modules/theme_region_test/theme_region_test.module17
-rw-r--r--core/modules/system/tests/modules/theme_suggestions_test/src/Hook/ThemeSuggestionsTestHooks.php4
-rw-r--r--core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php20
-rw-r--r--core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestHooks.php4
-rw-r--r--core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestThemeHooks.php4
-rw-r--r--core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestThemeHooks1.php62
-rw-r--r--core/modules/system/tests/modules/theme_test/theme_test.module41
-rw-r--r--core/modules/system/tests/modules/twig_loader_test/src/Loader/TestLoader.php2
-rw-r--r--core/modules/system/tests/modules/update_script_test/src/Hook/UpdateScriptTestRequirements.php11
-rw-r--r--core/modules/system/tests/modules/update_test_schema/src/Hook/UpdateTestSchemaRequirements.php3
-rw-r--r--core/modules/system/tests/src/Functional/Database/SelectTableSortDefaultTest.php4
-rw-r--r--core/modules/system/tests/src/Functional/Datetime/DrupalDateTimeTest.php108
-rw-r--r--core/modules/system/tests/src/Functional/Entity/EntityAddUITest.php15
-rw-r--r--core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php56
-rw-r--r--core/modules/system/tests/src/Functional/FileTransfer/FileTransferTest.php31
-rw-r--r--core/modules/system/tests/src/Functional/Form/ElementTest.php14
-rw-r--r--core/modules/system/tests/src/Functional/Form/FormTest.php36
-rw-r--r--core/modules/system/tests/src/Functional/Hook/HookCollectorPassTest.php61
-rw-r--r--core/modules/system/tests/src/Functional/Menu/LinksetControllerMultiLingualTest.php6
-rw-r--r--core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php2
-rw-r--r--core/modules/system/tests/src/Functional/Module/DependencyTest.php7
-rw-r--r--core/modules/system/tests/src/Functional/Module/GenericModuleTestBase.php10
-rw-r--r--core/modules/system/tests/src/Functional/Module/UninstallTest.php17
-rw-r--r--core/modules/system/tests/src/Functional/Module/VersionTest.php1
-rw-r--r--core/modules/system/tests/src/Functional/Pager/PagerTest.php2
-rw-r--r--core/modules/system/tests/src/Functional/ParamConverter/UpcastingTest.php14
-rw-r--r--core/modules/system/tests/src/Functional/SecurityAdvisories/SecurityAdvisoryTest.php35
-rw-r--r--core/modules/system/tests/src/Functional/Session/LegacySessionTest.php44
-rw-r--r--core/modules/system/tests/src/Functional/System/AccessDeniedTest.php2
-rw-r--r--core/modules/system/tests/src/Functional/System/PageTitleTest.php2
-rw-r--r--core/modules/system/tests/src/Functional/System/SitesDirectoryHardeningTest.php6
-rw-r--r--core/modules/system/tests/src/Functional/System/StatusTest.php2
-rw-r--r--core/modules/system/tests/src/Functional/Theme/FastTest.php52
-rw-r--r--core/modules/system/tests/src/Functional/UpdateSystem/UpdatePathTestJavaScriptTest.php2
-rw-r--r--core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php7
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/ActiveLinkTest.php4
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/Batch/ProcessingTest.php4
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/CopyFieldValueTest.php4
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/Form/ConfigTargetTest.php4
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/Form/DevelopmentSettingsFormTest.php8
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/Form/ElementsTableSelectTest.php6
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/Form/ElementsVerticalTabsWithSummaryTest.php4
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/Form/RebuildTest.php3
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/Form/TriggeringElementTest.php4
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/FrameworkTest.php4
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/ModalRendererTest.php4
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/ModuleFilterTest.php6
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/ModuleUninstallFilterTest.php4
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php13
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/System/DateFormatTest.php4
-rw-r--r--core/modules/system/tests/src/FunctionalJavascript/ThemeSettingsFormTest.php8
-rw-r--r--core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php11
-rw-r--r--core/modules/system/tests/src/Kernel/Element/StatusReportPageTest.php58
-rw-r--r--core/modules/system/tests/src/Kernel/Migrate/d7/MigrateSystemConfigurationTest.php3
-rw-r--r--core/modules/system/tests/src/Kernel/Module/RequirementsTest.php3
-rw-r--r--core/modules/system/tests/src/Kernel/System/CronQueueTest.php3
-rw-r--r--core/modules/system/tests/src/Kernel/System/RunTimeRequirementsTest.php5
-rw-r--r--core/modules/system/tests/src/Unit/Event/SecurityFileUploadEventSubscriberTest.php17
-rw-r--r--core/modules/system/tests/src/Unit/Pager/PreprocessPagerTest.php55
-rw-r--r--core/modules/system/tests/themes/test_theme/test_theme.theme4
156 files changed, 3569 insertions, 2952 deletions
diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml
index 88f34652b98..854ef2a178e 100644
--- a/core/modules/system/config/schema/system.schema.yml
+++ b/core/modules/system/config/schema/system.schema.yml
@@ -69,14 +69,14 @@ system.cron:
type: integer
label: 'Requirements warning period'
constraints:
- # @see system_requirements()
+ # @see \Drupal\system\Hook\SystemRequirementsHooks
Range:
min: 60
requirements_error:
type: integer
label: 'Requirements error period'
constraints:
- # @see system_requirements()
+ # @see \Drupal\system\Hook\SystemRequirementsHooks
Range:
min: 300
logging:
@@ -157,15 +157,13 @@ system.diff:
label: 'Number of leading lines in a diff'
constraints:
# @see \Drupal\Component\Diff\DiffFormatter
- Range:
- min: 0
+ PositiveOrZero: ~
lines_trailing:
type: integer
label: 'Number of trailing lines in a diff'
constraints:
# @see \Drupal\Component\Diff\DiffFormatter
- Range:
- min: 0
+ PositiveOrZero: ~
system.logging:
type: config_object
@@ -355,8 +353,7 @@ system.file:
type: integer
label: 'Maximum age for temporary files'
constraints:
- Range:
- min: 0
+ PositiveOrZero: ~
system.image:
type: config_object
@@ -426,8 +423,7 @@ system.advisories:
# Minimum can be set to 0 as it just means the advisories will be retrieved on every call.
# @see \Drupal\system\SecurityAdvisories\SecurityAdvisoriesFetcher::getSecurityAdvisories
constraints:
- Range:
- min: 0
+ PositiveOrZero: ~
block.settings.system_branding_block:
type: block_settings
diff --git a/core/modules/system/css/components/item-list.module.css b/core/modules/system/css/components/item-list.module.css
deleted file mode 100644
index 2d23ee5bd33..00000000000
--- a/core/modules/system/css/components/item-list.module.css
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * @file
- * Styles for item list.
- */
-
-.item-list__comma-list,
-.item-list__comma-list li {
- display: inline;
-}
-.item-list__comma-list {
- margin: 0;
- padding: 0;
-}
-.item-list__comma-list li::after {
- content: ", ";
-}
-.item-list__comma-list li:last-child::after {
- content: "";
-}
diff --git a/core/modules/system/css/components/position-container.module.css b/core/modules/system/css/components/position-container.module.css
deleted file mode 100644
index ae209f3aa61..00000000000
--- a/core/modules/system/css/components/position-container.module.css
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * @file
- * Contain positioned elements.
- */
-
-.position-container {
- position: relative;
-}
diff --git a/core/modules/system/css/components/reset-appearance.module.css b/core/modules/system/css/components/reset-appearance.module.css
deleted file mode 100644
index 59741a85ce8..00000000000
--- a/core/modules/system/css/components/reset-appearance.module.css
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * @file
- * Utility class to remove browser styles, especially for button.
- */
-
-.reset-appearance {
- margin: 0;
- padding: 0;
- border: 0 none;
- background: transparent;
- line-height: inherit;
- -webkit-appearance: none;
- appearance: none;
-}
diff --git a/core/modules/system/css/components/system-status-report-counters.css b/core/modules/system/css/components/system-status-report-counters.css
index 7040c257a0f..54ffabe5fc1 100644
--- a/core/modules/system/css/components/system-status-report-counters.css
+++ b/core/modules/system/css/components/system-status-report-counters.css
@@ -9,7 +9,7 @@
padding: 0.5em 0;
text-align: center;
white-space: nowrap;
- background-color: rgba(0, 0, 0, 0.063);
+ background-color: rgb(0, 0, 0, 0.063);
}
@media screen and (min-width: 60em) {
diff --git a/core/modules/system/src/Controller/DbUpdateController.php b/core/modules/system/src/Controller/DbUpdateController.php
index 131b6a075d5..4fdb93c8b45 100644
--- a/core/modules/system/src/Controller/DbUpdateController.php
+++ b/core/modules/system/src/Controller/DbUpdateController.php
@@ -7,6 +7,7 @@ use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
use Drupal\Core\Render\BareHtmlPageRendererInterface;
use Drupal\Core\Session\AccountInterface;
@@ -166,8 +167,8 @@ class DbUpdateController extends ControllerBase {
$regions = [];
$requirements = update_check_requirements();
- $severity = drupal_requirements_severity($requirements);
- if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && !$request->getSession()->has('update_ignore_warnings'))) {
+ $severity = RequirementSeverity::maxSeverityFromRequirements($requirements);
+ if ($severity === RequirementSeverity::Error || ($severity === RequirementSeverity::Warning && !$request->getSession()->has('update_ignore_warnings'))) {
$regions['sidebar_first'] = $this->updateTasksList('requirements');
$output = $this->requirements($severity, $requirements, $request);
}
@@ -227,7 +228,7 @@ class DbUpdateController extends ControllerBase {
$this->keyValueExpirableFactory->get('update_available_release')->deleteAll();
$build['info_header'] = [
- '#markup' => '<p>' . $this->t('Use this utility to update your database whenever a module, theme, or the core software is updated.') . '</p><p>' . $this->t('For more detailed information, see the <a href="https://www.drupal.org/upgrade">upgrading handbook</a>. If you are unsure what these terms mean you should probably contact your hosting provider.') . '</p>',
+ '#markup' => '<p>' . $this->t('Use this utility to update your database whenever a module, theme, or the core software is updated.') . '</p><p>' . $this->t('For more detailed information, see the <a href="https://www.drupal.org/docs/updating-drupal">Updating Drupal guide</a>. If you are unsure what these terms mean you should probably contact your hosting provider.') . '</p>',
];
$info[] = $this->t("<strong>Back up your code</strong>. Hint: when backing up module code, do not leave that backup in the 'modules' or 'sites/*/modules' directories as this may confuse Drupal's auto-discovery mechanism.");
@@ -543,7 +544,7 @@ class DbUpdateController extends ControllerBase {
* A render array.
*/
public function requirements($severity, array $requirements, Request $request) {
- $options = $severity == REQUIREMENT_WARNING ? ['continue' => 1] : [];
+ $options = $severity === RequirementSeverity::Warning ? ['continue' => 1] : [];
// @todo Revisit once https://www.drupal.org/node/2548095 is in. Something
// like Url::fromRoute('system.db_update')->setOptions() should then be
// possible.
@@ -704,16 +705,20 @@ class DbUpdateController extends ControllerBase {
'title' => $this->t('Front page'),
'url' => Url::fromRoute('<front>')->setOption('base_url', $base_url),
];
- if ($this->account->hasPermission('access administration pages')) {
+
+ $admin_url = Url::fromRoute('system.admin')->setOption('base_url', $base_url);
+ if ($admin_url->access($this->account)) {
$links['admin-pages'] = [
'title' => $this->t('Administration pages'),
- 'url' => Url::fromRoute('system.admin')->setOption('base_url', $base_url),
+ 'url' => $admin_url,
];
}
- if ($this->account->hasPermission('administer site configuration')) {
+
+ $status_report_url = Url::fromRoute('system.status')->setOption('base_url', $base_url);
+ if ($status_report_url->access($this->account)) {
$links['status-report'] = [
'title' => $this->t('Status report'),
- 'url' => Url::fromRoute('system.status')->setOption('base_url', $base_url),
+ 'url' => $status_report_url,
];
}
return $links;
diff --git a/core/modules/system/src/Controller/SystemController.php b/core/modules/system/src/Controller/SystemController.php
index b340555c628..fc253ac2f7c 100644
--- a/core/modules/system/src/Controller/SystemController.php
+++ b/core/modules/system/src/Controller/SystemController.php
@@ -280,8 +280,6 @@ class SystemController extends ControllerBase {
continue;
}
- // @todo Add logic for not displaying hidden modules in
- // https://drupal.org/node/3117829.
$module_name = $modules[$dependency]->info['name'];
$theme->module_dependencies_list[$dependency] = $modules[$dependency]->status ? $this->t('@module_name', ['@module_name' => $module_name]) : $this->t('@module_name (<span class="admin-disabled">disabled</span>)', ['@module_name' => $module_name]);
diff --git a/core/modules/system/src/CronController.php b/core/modules/system/src/CronController.php
index 6ba1e031a4d..59bc6e9289a 100644
--- a/core/modules/system/src/CronController.php
+++ b/core/modules/system/src/CronController.php
@@ -37,7 +37,7 @@ class CronController extends ControllerBase {
public function run() {
$this->cron->run();
- // HTTP 204 is "No content", meaning "I did what you asked and we're done."
+ // HTTP 204 is "No content", meaning "I did what you asked and we're done.".
return new Response('', 204);
}
diff --git a/core/modules/system/src/Element/StatusReportPage.php b/core/modules/system/src/Element/StatusReportPage.php
index 90a878831ea..2d6494f2fe3 100644
--- a/core/modules/system/src/Element/StatusReportPage.php
+++ b/core/modules/system/src/Element/StatusReportPage.php
@@ -2,9 +2,9 @@
namespace Drupal\system\Element;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\Render\Attribute\RenderElement;
use Drupal\Core\Render\Element\RenderElementBase;
-use Drupal\Core\Render\Element\StatusReport;
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
/**
@@ -37,6 +37,7 @@ class StatusReportPage extends RenderElementBase {
'#theme' => 'status_report_general_info',
];
// Loop through requirements and pull out items.
+ RequirementSeverity::convertLegacyIntSeveritiesToEnums($element['#requirements'], __METHOD__);
foreach ($element['#requirements'] as $key => $requirement) {
switch ($key) {
case 'cron':
@@ -59,10 +60,10 @@ class StatusReportPage extends RenderElementBase {
case 'php':
case 'php_memory_limit':
$element['#general_info']['#' . $key] = $requirement;
- if (isset($requirement['severity']) && $requirement['severity'] < REQUIREMENT_WARNING) {
- if (empty($requirement['severity']) || $requirement['severity'] == REQUIREMENT_OK) {
- unset($element['#requirements'][$key]);
- }
+ if (isset($requirement['severity']) &&
+ in_array($requirement['severity'], [RequirementSeverity::Info, RequirementSeverity::OK], TRUE)
+ ) {
+ unset($element['#requirements'][$key]);
}
break;
}
@@ -94,18 +95,18 @@ class StatusReportPage extends RenderElementBase {
],
];
- $severities = StatusReport::getSeverities();
+ RequirementSeverity::convertLegacyIntSeveritiesToEnums($element['#requirements'], __METHOD__);
foreach ($element['#requirements'] as $key => &$requirement) {
- $severity = $severities[REQUIREMENT_INFO];
+ $severity = RequirementSeverity::Info;
if (isset($requirement['severity'])) {
- $severity = $severities[(int) $requirement['severity']];
+ $severity = $requirement['severity'];
}
elseif (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install') {
- $severity = $severities[REQUIREMENT_OK];
+ $severity = RequirementSeverity::OK;
}
- if (isset($counters[$severity['status']])) {
- $counters[$severity['status']]['amount']++;
+ if (isset($counters[$severity->status()])) {
+ $counters[$severity->status()]['amount']++;
}
}
diff --git a/core/modules/system/src/EventSubscriber/SecurityFileUploadEventSubscriber.php b/core/modules/system/src/EventSubscriber/SecurityFileUploadEventSubscriber.php
index 737b9a7a53e..c9722a5e4e1 100644
--- a/core/modules/system/src/EventSubscriber/SecurityFileUploadEventSubscriber.php
+++ b/core/modules/system/src/EventSubscriber/SecurityFileUploadEventSubscriber.php
@@ -52,22 +52,17 @@ class SecurityFileUploadEventSubscriber implements EventSubscriberInterface {
// http://php.net/manual/security.filesystem.nullbytes.php
$filename = str_replace(chr(0), '', $filename);
+ if ($filename !== $event->getFilename()) {
+ $event->setFilename($filename)->setSecurityRename();
+ }
+
// Split up the filename by periods. The first part becomes the basename,
// the last part the final extension.
$filename_parts = explode('.', $filename);
// Remove file basename.
$filename = array_shift($filename_parts);
- // Remove final extension.
+ // Remove final extension. In the case of dot filenames this will be empty.
$final_extension = (string) array_pop($filename_parts);
- // Check if we're dealing with a dot file that is also an insecure extension
- // e.g. .htaccess. In this scenario there is only one 'part' and the
- // extension becomes the filename. We use the original filename from the
- // event rather than the trimmed version above.
- $insecure_uploads = $this->configFactory->get('system.file')->get('allow_insecure_uploads');
- if (!$insecure_uploads && $final_extension === '' && str_contains($event->getFilename(), '.') && in_array(strtolower($filename), FileSystemInterface::INSECURE_EXTENSIONS, TRUE)) {
- $final_extension = $filename;
- $filename = '';
- }
$extensions = $event->getAllowedExtensions();
if (!empty($extensions) && !in_array(strtolower($final_extension), $extensions, TRUE)) {
@@ -81,7 +76,7 @@ class SecurityFileUploadEventSubscriber implements EventSubscriberInterface {
return;
}
- if (!$insecure_uploads && in_array(strtolower($final_extension), FileSystemInterface::INSECURE_EXTENSIONS, TRUE)) {
+ if (!$this->configFactory->get('system.file')->get('allow_insecure_uploads') && in_array(strtolower($final_extension), FileSystemInterface::INSECURE_EXTENSIONS, TRUE)) {
if (empty($extensions) || in_array('txt', $extensions, TRUE)) {
// Add .txt to potentially executable files prior to munging to help
// prevent exploits. This results in a filenames like filename.php being
diff --git a/core/modules/system/src/Form/ModulesListForm.php b/core/modules/system/src/Form/ModulesListForm.php
index 90dc9ead38b..551e813c611 100644
--- a/core/modules/system/src/Form/ModulesListForm.php
+++ b/core/modules/system/src/Form/ModulesListForm.php
@@ -329,7 +329,7 @@ class ModulesListForm extends FormBase {
// Disable the checkbox for required modules.
if (!empty($module->info['required'])) {
// Used when displaying modules that are required by the installation
- // profile
+ // profile.
$row['enable']['#disabled'] = TRUE;
$row['#required_by'][] = $distribution . (!empty($module->info['explanation']) ? ' (' . $module->info['explanation'] . ')' : '');
}
@@ -375,8 +375,6 @@ class ModulesListForm extends FormBase {
// If this module requires other modules, add them to the array.
/** @var \Drupal\Core\Extension\Dependency $dependency_object */
foreach ($module->requires as $dependency => $dependency_object) {
- // @todo Add logic for not displaying hidden modules in
- // https://drupal.org/node/3117829.
if ($incompatible = $this->checkDependencyMessage($modules, $dependency, $dependency_object)) {
$row['#requires'][$dependency] = $incompatible;
$row['enable']['#disabled'] = TRUE;
diff --git a/core/modules/system/src/Form/ModulesUninstallForm.php b/core/modules/system/src/Form/ModulesUninstallForm.php
index c999f37fe23..9d51b67f2d3 100644
--- a/core/modules/system/src/Form/ModulesUninstallForm.php
+++ b/core/modules/system/src/Form/ModulesUninstallForm.php
@@ -106,7 +106,8 @@ class ModulesUninstallForm extends FormBase {
include_once DRUPAL_ROOT . '/core/includes/install.inc';
// Get a list of all available modules that can be uninstalled.
- $uninstallable = array_filter($this->moduleExtensionList->getList(), function ($module) {
+ $modules = $this->moduleExtensionList->getList();
+ $uninstallable = array_filter($modules, function ($module) {
return empty($module->info['required']) && $module->status;
});
@@ -137,7 +138,7 @@ class ModulesUninstallForm extends FormBase {
$form['modules'] = [];
// Only build the rest of the form if there are any modules available to
- // uninstall;
+ // uninstall.
if (empty($uninstallable)) {
return $form;
}
@@ -199,7 +200,13 @@ class ModulesUninstallForm extends FormBase {
// we can allow this module to be uninstalled.
foreach (array_keys($module->required_by) as $dependent) {
if ($this->updateRegistry->getInstalledVersion($dependent) !== $this->updateRegistry::SCHEMA_UNINSTALLED) {
- $form['modules'][$module->getName()]['#required_by'][] = $dependent;
+ $module_name = $modules[$dependent]->info['name'];
+ if ($dependent != strtolower(str_replace(' ', '_', $module_name))) {
+ $form['modules'][$module->getName()]['#required_by'][] = $module_name . " (" . $dependent . ")";
+ }
+ else {
+ $form['modules'][$module->getName()]['#required_by'][] = $module_name;
+ }
$form['uninstall'][$module->getName()]['#disabled'] = TRUE;
}
}
diff --git a/core/modules/system/src/Form/ThemeSettingsForm.php b/core/modules/system/src/Form/ThemeSettingsForm.php
index 9133eb8aa10..44f44179f5a 100644
--- a/core/modules/system/src/Form/ThemeSettingsForm.php
+++ b/core/modules/system/src/Form/ThemeSettingsForm.php
@@ -140,7 +140,8 @@ class ThemeSettingsForm extends ConfigFormBase {
$themes = $this->themeHandler->listInfo();
- // Default settings are defined in theme_get_setting() in includes/theme.inc
+ // Default settings are defined in theme_get_setting() in
+ // includes/theme.inc.
if ($theme) {
if (!$this->themeHandler->hasUi($theme)) {
throw new NotFoundHttpException();
@@ -168,7 +169,7 @@ class ThemeSettingsForm extends ConfigFormBase {
'#value' => $config_key,
];
- // Toggle settings
+ // Toggle settings.
$toggles = [
'node_user_picture' => $this->t('User pictures in posts'),
'comment_user_picture' => $this->t('User pictures in comments'),
@@ -176,7 +177,7 @@ class ThemeSettingsForm extends ConfigFormBase {
'favicon' => $this->t('Shortcut icon'),
];
- // Some features are not always available
+ // Some features are not always available.
$disabled = [];
if (!user_picture_enabled()) {
$disabled['toggle_node_user_picture'] = TRUE;
diff --git a/core/modules/system/src/Hook/PageAttachmentsHook.php b/core/modules/system/src/Hook/PageAttachmentsHook.php
index fb6335f90c3..3f271571ede 100644
--- a/core/modules/system/src/Hook/PageAttachmentsHook.php
+++ b/core/modules/system/src/Hook/PageAttachmentsHook.php
@@ -19,7 +19,7 @@ final class PageAttachmentsHook {
/**
* Implements hook_page_attachments().
*
- * @see template_preprocess_maintenance_page()
+ * @see \Drupal\Core\Theme\ThemePreprocess::preprocessMaintenancePage()
* @see \Drupal\Core\EventSubscriber\ActiveLinkResponseFilter
*/
#[Hook('page_attachments')]
diff --git a/core/modules/system/src/Hook/SystemHooks.php b/core/modules/system/src/Hook/SystemHooks.php
index ae3e8d71074..e1be05077e2 100644
--- a/core/modules/system/src/Hook/SystemHooks.php
+++ b/core/modules/system/src/Hook/SystemHooks.php
@@ -159,10 +159,6 @@ class SystemHooks {
}
/**
- * @} End of "defgroup authorize".
- */
-
- /**
* Implements hook_updater_info().
*/
#[Hook('updater_info')]
@@ -276,7 +272,11 @@ class SystemHooks {
// before doing so. Also add the loaded libraries to ajaxPageState.
/** @var \Drupal\Core\Asset\LibraryDependencyResolver $library_dependency_resolver */
$library_dependency_resolver = \Drupal::service('library.dependency_resolver');
- if (isset($settings['ajaxPageState']) || in_array('core/drupal.ajax', $library_dependency_resolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()))) {
+ $loaded_libraries = [];
+ if (!isset($settings['ajaxPageState'])) {
+ $loaded_libraries = $library_dependency_resolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries());
+ }
+ if (isset($settings['ajaxPageState']) || in_array('core/drupal.ajax', $loaded_libraries) || in_array('core/drupal.htmx', $loaded_libraries)) {
if (!defined('MAINTENANCE_MODE')) {
// The theme token is only validated when the theme requested is not the
// default, so don't generate it unless necessary.
@@ -339,7 +339,7 @@ class SystemHooks {
\Drupal::service('file.htaccess_writer')->ensure();
if (\Drupal::config('system.advisories')->get('enabled')) {
// Fetch the security advisories so that they will be pre-fetched during
- // _system_advisories_requirements() and system_page_top().
+ // systemAdvisoriesRequirements() and system_page_top().
/** @var \Drupal\system\SecurityAdvisories\SecurityAdvisoriesFetcher $fetcher */
$fetcher = \Drupal::service('system.sa_fetcher');
$fetcher->getSecurityAdvisories();
diff --git a/core/modules/system/src/Hook/SystemRequirementsHooks.php b/core/modules/system/src/Hook/SystemRequirementsHooks.php
new file mode 100644
index 00000000000..49d318eb9bf
--- /dev/null
+++ b/core/modules/system/src/Hook/SystemRequirementsHooks.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Drupal\system\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\system\Install\Requirements\SystemRequirements;
+
+/**
+ * Requirements hook implementations for system module.
+ */
+class SystemRequirementsHooks {
+
+ /**
+ * Implements hook_update_requirements().
+ */
+ #[Hook('update_requirements')]
+ public function updateRequirements(): array {
+ return SystemRequirements::checkRequirements('update');
+ }
+
+ /**
+ * Implements hook_runtime_requirements().
+ */
+ #[Hook('runtime_requirements')]
+ public function runtimeRequirements(): array {
+ return SystemRequirements::checkRequirements('runtime');
+ }
+
+}
diff --git a/core/modules/system/src/Hook/SystemThemeHooks.php b/core/modules/system/src/Hook/SystemThemeHooks.php
new file mode 100644
index 00000000000..728a02f7498
--- /dev/null
+++ b/core/modules/system/src/Hook/SystemThemeHooks.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace Drupal\system\Hook;
+
+use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for system.
+ */
+class SystemThemeHooks {
+
+ /**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+ #[Hook('theme_suggestions_html')]
+ public function themeSuggestionsHtml(array $variables): array {
+ $path_args = explode('/', trim(\Drupal::service('path.current')->getPath(), '/'));
+ return theme_get_suggestions($path_args, 'html');
+ }
+
+ /**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+ #[Hook('theme_suggestions_page')]
+ public function themeSuggestionsPage(array $variables): array {
+ $path_args = explode('/', trim(\Drupal::service('path.current')->getPath(), '/'));
+ $suggestions = theme_get_suggestions($path_args, 'page');
+ $supported_http_error_codes = [
+ 401,
+ 403,
+ 404,
+ ];
+ $exception = \Drupal::requestStack()->getCurrentRequest()->attributes->get('exception');
+ if ($exception instanceof HttpExceptionInterface && in_array($exception->getStatusCode(), $supported_http_error_codes, TRUE)) {
+ $suggestions[] = 'page__4xx';
+ $suggestions[] = 'page__' . $exception->getStatusCode();
+ }
+ return $suggestions;
+ }
+
+ /**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+ #[Hook('theme_suggestions_maintenance_page')]
+ public function themeSuggestionsMaintenancePage(array $variables): array {
+ $suggestions = [];
+ // Dead databases will show error messages so supplying this template will
+ // allow themers to override the page and the content completely.
+ $offline = defined('MAINTENANCE_MODE');
+ try {
+ \Drupal::service('path.matcher')->isFrontPage();
+ }
+ catch (\Exception) {
+ // The database is not yet available.
+ $offline = TRUE;
+ }
+ if ($offline) {
+ $suggestions[] = 'maintenance_page__offline';
+ }
+ return $suggestions;
+ }
+
+ /**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+ #[Hook('theme_suggestions_region')]
+ public function themeSuggestionsRegion(array $variables): array {
+ $suggestions = [];
+ if (!empty($variables['elements']['#region'])) {
+ $suggestions[] = 'region__' . $variables['elements']['#region'];
+ }
+ return $suggestions;
+ }
+
+ /**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+ #[Hook('theme_suggestions_field')]
+ public function themeSuggestionsField(array $variables): array {
+ $suggestions = [];
+ $element = $variables['element'];
+ $suggestions[] = 'field__' . $element['#field_type'];
+ $suggestions[] = 'field__' . $element['#field_name'];
+ $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#bundle'];
+ $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'];
+ $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'] . '__' . $element['#bundle'];
+ return $suggestions;
+ }
+
+ /**
+ * @} End of "defgroup authorize".
+ */
+
+ /**
+ * Implements hook_preprocess_HOOK() for block templates.
+ */
+ #[Hook('preprocess_block')]
+ public function preprocessBlock(&$variables): void {
+ switch ($variables['base_plugin_id']) {
+ case 'system_branding_block':
+ $variables['site_logo'] = '';
+ if ($variables['content']['site_logo']['#access'] && $variables['content']['site_logo']['#uri']) {
+ $variables['site_logo'] = $variables['content']['site_logo']['#uri'];
+ }
+ $variables['site_name'] = '';
+ if ($variables['content']['site_name']['#access'] && $variables['content']['site_name']['#markup']) {
+ $variables['site_name'] = $variables['content']['site_name']['#markup'];
+ }
+ $variables['site_slogan'] = '';
+ if ($variables['content']['site_slogan']['#access'] && $variables['content']['site_slogan']['#markup']) {
+ $variables['site_slogan'] = [
+ '#markup' => $variables['content']['site_slogan']['#markup'],
+ ];
+ }
+ break;
+ }
+ }
+
+ /**
+ * Implements hook_preprocess_toolbar().
+ */
+ #[Hook('preprocess_toolbar')]
+ public function preprocessToolbar(array &$variables, $hook, $info): void {
+ // When Claro is the admin theme, Claro overrides the active theme's if that
+ // active theme is not Claro. Because of these potential overrides, the
+ // toolbar cache should be invalidated any time the default or admin theme
+ // changes.
+ $variables['#cache']['tags'][] = 'config:system.theme';
+ // If Claro is the admin theme but not the active theme, still include
+ // Claro's toolbar preprocessing.
+ if (_system_is_claro_admin_and_not_active()) {
+ require_once DRUPAL_ROOT . '/core/themes/claro/claro.theme';
+ claro_preprocess_toolbar($variables, $hook, $info);
+ }
+ }
+
+}
diff --git a/core/modules/system/src/Install/Requirements/SystemRequirements.php b/core/modules/system/src/Install/Requirements/SystemRequirements.php
new file mode 100644
index 00000000000..ee6a1d7a802
--- /dev/null
+++ b/core/modules/system/src/Install/Requirements/SystemRequirements.php
@@ -0,0 +1,1665 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\system\Install\Requirements;
+
+use Drupal\Core\Extension\InstallRequirementsInterface;
+use Drupal\Component\FileSystem\FileSystem as FileSystemComponent;
+use Drupal\Component\Utility\Bytes;
+use Drupal\Component\Utility\Environment;
+use Drupal\Component\Utility\OpCodeCache;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Database\Database;
+use Drupal\Core\DrupalKernel;
+use Drupal\Core\Extension\ExtensionLifecycle;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
+use Drupal\Core\File\FileSystemInterface;
+use Drupal\Core\Link;
+use Drupal\Core\Render\Markup;
+use Drupal\Core\Site\Settings;
+use Drupal\Core\StreamWrapper\PrivateStream;
+use Drupal\Core\StreamWrapper\PublicStream;
+use Drupal\Core\StringTranslation\ByteSizeMarkup;
+use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\Url;
+use Drupal\Core\Utility\Error;
+use Drupal\Core\Utility\PhpRequirements;
+use Psr\Http\Client\ClientExceptionInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Install time requirements for the system module.
+ */
+class SystemRequirements implements InstallRequirementsInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getRequirements(): array {
+ return self::checkRequirements('install');
+ }
+
+ /**
+ * Check requirements for a given phase.
+ *
+ * @param string $phase
+ * The phase in which requirements are checked, as documented in
+ * hook_runtime_requirements() and hook_update_requirements().
+ *
+ * @return array
+ * An associative array of requirements, as documented in
+ * hook_runtime_requirements() and hook_update_requirements().
+ */
+ public static function checkRequirements(string $phase): array {
+ global $install_state;
+
+ // Get the current default PHP requirements for this version of Drupal.
+ $minimum_supported_php = PhpRequirements::getMinimumSupportedPhp();
+
+ // Reset the extension lists.
+ /** @var \Drupal\Core\Extension\ModuleExtensionList $module_extension_list */
+ $module_extension_list = \Drupal::service('extension.list.module');
+ $module_extension_list->reset();
+ /** @var \Drupal\Core\Extension\ThemeExtensionList $theme_extension_list */
+ $theme_extension_list = \Drupal::service('extension.list.theme');
+ $theme_extension_list->reset();
+ $requirements = [];
+
+ // Report Drupal version
+ if ($phase == 'runtime') {
+ $requirements['drupal'] = [
+ 'title' => t('Drupal'),
+ 'value' => \Drupal::VERSION,
+ 'severity' => RequirementSeverity::Info,
+ 'weight' => -10,
+ ];
+
+ // Display the currently active installation profile, if the site
+ // is not running the default installation profile.
+ $profile = \Drupal::installProfile();
+ if ($profile != 'standard' && !empty($profile)) {
+ $info = $module_extension_list->getExtensionInfo($profile);
+ $requirements['install_profile'] = [
+ 'title' => t('Installation profile'),
+ 'value' => t('%profile_name (%profile%version)', [
+ '%profile_name' => $info['name'],
+ '%profile' => $profile,
+ '%version' => !empty($info['version']) ? '-' . $info['version'] : '',
+ ]),
+ 'severity' => RequirementSeverity::Info,
+ 'weight' => -9,
+ ];
+ }
+
+ // Gather all obsolete and experimental modules being enabled.
+ $obsolete_extensions = [];
+ $deprecated_modules = [];
+ $experimental_modules = [];
+ $enabled_modules = \Drupal::moduleHandler()->getModuleList();
+ foreach ($enabled_modules as $module => $data) {
+ $info = $module_extension_list->getExtensionInfo($module);
+ if (isset($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER])) {
+ if ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::EXPERIMENTAL) {
+ $experimental_modules[$module] = $info['name'];
+ }
+ elseif ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::DEPRECATED) {
+ $deprecated_modules[] = ['name' => $info['name'], 'lifecycle_link' => $info['lifecycle_link']];
+ }
+ elseif ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::OBSOLETE) {
+ $obsolete_extensions[$module] = ['name' => $info['name'], 'lifecycle_link' => $info['lifecycle_link']];
+ }
+ }
+ }
+
+ // Warn if any experimental modules are installed.
+ if (!empty($experimental_modules)) {
+ $requirements['experimental_modules'] = [
+ 'title' => t('Experimental modules installed'),
+ 'value' => t('Experimental modules found: %module_list. <a href=":url">Experimental modules</a> are provided for testing purposes only. Use at your own risk.', ['%module_list' => implode(', ', $experimental_modules), ':url' => 'https://www.drupal.org/core/experimental']),
+ 'severity' => RequirementSeverity::Warning,
+ ];
+ }
+ // Warn if any deprecated modules are installed.
+ if (!empty($deprecated_modules)) {
+ foreach ($deprecated_modules as $deprecated_module) {
+ $deprecated_modules_link_list[] = (string) Link::fromTextAndUrl($deprecated_module['name'], Url::fromUri($deprecated_module['lifecycle_link']))->toString();
+ }
+ $requirements['deprecated_modules'] = [
+ 'title' => t('Deprecated modules installed'),
+ 'value' => t('Deprecated modules found: %module_list.', [
+ '%module_list' => Markup::create(implode(', ', $deprecated_modules_link_list)),
+ ]),
+ 'severity' => RequirementSeverity::Warning,
+ ];
+ }
+
+ // Gather all obsolete and experimental themes being installed.
+ $experimental_themes = [];
+ $deprecated_themes = [];
+ $installed_themes = \Drupal::service('theme_handler')->listInfo();
+ foreach ($installed_themes as $theme => $data) {
+ $info = $theme_extension_list->getExtensionInfo($theme);
+ if (isset($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER])) {
+ if ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::EXPERIMENTAL) {
+ $experimental_themes[$theme] = $info['name'];
+ }
+ elseif ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::DEPRECATED) {
+ $deprecated_themes[] = ['name' => $info['name'], 'lifecycle_link' => $info['lifecycle_link']];
+ }
+ elseif ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::OBSOLETE) {
+ $obsolete_extensions[$theme] = ['name' => $info['name'], 'lifecycle_link' => $info['lifecycle_link']];
+ }
+ }
+ }
+
+ // Warn if any experimental themes are installed.
+ if (!empty($experimental_themes)) {
+ $requirements['experimental_themes'] = [
+ 'title' => t('Experimental themes installed'),
+ 'value' => t('Experimental themes found: %theme_list. Experimental themes are provided for testing purposes only. Use at your own risk.', ['%theme_list' => implode(', ', $experimental_themes)]),
+ 'severity' => RequirementSeverity::Warning,
+ ];
+ }
+
+ // Warn if any deprecated themes are installed.
+ if (!empty($deprecated_themes)) {
+ foreach ($deprecated_themes as $deprecated_theme) {
+ $deprecated_themes_link_list[] = (string) Link::fromTextAndUrl($deprecated_theme['name'], Url::fromUri($deprecated_theme['lifecycle_link']))->toString();
+
+ }
+ $requirements['deprecated_themes'] = [
+ 'title' => t('Deprecated themes installed'),
+ 'value' => t('Deprecated themes found: %theme_list.', [
+ '%theme_list' => Markup::create(implode(', ', $deprecated_themes_link_list)),
+ ]),
+ 'severity' => RequirementSeverity::Warning,
+ ];
+ }
+
+ // Warn if any obsolete extensions (themes or modules) are installed.
+ if (!empty($obsolete_extensions)) {
+ foreach ($obsolete_extensions as $obsolete_extension) {
+ $obsolete_extensions_link_list[] = (string) Link::fromTextAndUrl($obsolete_extension['name'], Url::fromUri($obsolete_extension['lifecycle_link']))->toString();
+ }
+ $requirements['obsolete_extensions'] = [
+ 'title' => t('Obsolete extensions installed'),
+ 'value' => t('Obsolete extensions found: %extensions. Obsolete extensions are provided only so that they can be uninstalled cleanly. You should immediately <a href=":uninstall_url">uninstall these extensions</a> since they may be removed in a future release.', [
+ '%extensions' => Markup::create(implode(', ', $obsolete_extensions_link_list)),
+ ':uninstall_url' => Url::fromRoute('system.modules_uninstall')->toString(),
+ ]),
+ 'severity' => RequirementSeverity::Warning,
+ ];
+ }
+ self::systemAdvisoriesRequirements($requirements);
+ }
+
+ // Web server information.
+ $request_object = \Drupal::request();
+ $software = $request_object->server->get('SERVER_SOFTWARE');
+ $requirements['webserver'] = [
+ 'title' => t('Web server'),
+ 'value' => $software,
+ ];
+
+ // Tests clean URL support.
+ if ($phase == 'install' && $install_state['interactive'] && !$request_object->query->has('rewrite') && str_contains($software, 'Apache')) {
+ // If the Apache rewrite module is not enabled, Apache version must be >=
+ // 2.2.16 because of the FallbackResource directive in the root .htaccess
+ // file. Since the Apache version reported by the server is dependent on
+ // the ServerTokens setting in httpd.conf, we may not be able to
+ // determine if a given config is valid. Thus we are unable to use
+ // version_compare() as we need have three possible outcomes: the version
+ // of Apache is greater than 2.2.16, is less than 2.2.16, or cannot be
+ // determined accurately. In the first case, we encourage the use of
+ // mod_rewrite; in the second case, we raise an error regarding the
+ // minimum Apache version; in the third case, we raise a warning that the
+ // current version of Apache may not be supported.
+ $rewrite_warning = FALSE;
+ $rewrite_error = FALSE;
+ $apache_version_string = 'Apache';
+
+ // Determine the Apache version number: major, minor and revision.
+ if (preg_match('/Apache\/(\d+)\.?(\d+)?\.?(\d+)?/', $software, $matches)) {
+ $apache_version_string = $matches[0];
+
+ // Major version number
+ if ($matches[1] < 2) {
+ $rewrite_error = TRUE;
+ }
+ elseif ($matches[1] == 2) {
+ if (!isset($matches[2])) {
+ $rewrite_warning = TRUE;
+ }
+ elseif ($matches[2] < 2) {
+ $rewrite_error = TRUE;
+ }
+ elseif ($matches[2] == 2) {
+ if (!isset($matches[3])) {
+ $rewrite_warning = TRUE;
+ }
+ elseif ($matches[3] < 16) {
+ $rewrite_error = TRUE;
+ }
+ }
+ }
+ }
+ else {
+ $rewrite_warning = TRUE;
+ }
+
+ if ($rewrite_warning) {
+ $requirements['apache_version'] = [
+ 'title' => t('Apache version'),
+ 'value' => $apache_version_string,
+ 'severity' => RequirementSeverity::Warning,
+ 'description' => t('Due to the settings for ServerTokens in httpd.conf, it is impossible to accurately determine the version of Apache running on this server. The reported value is @reported, to run Drupal without mod_rewrite, a minimum version of 2.2.16 is needed.', ['@reported' => $apache_version_string]),
+ ];
+ }
+
+ if ($rewrite_error) {
+ $requirements['Apache version'] = [
+ 'title' => t('Apache version'),
+ 'value' => $apache_version_string,
+ 'severity' => RequirementSeverity::Error,
+ 'description' => t('The minimum version of Apache needed to run Drupal without mod_rewrite enabled is 2.2.16. See the <a href=":link">enabling clean URLs</a> page for more information on mod_rewrite.', [':link' => 'https://www.drupal.org/docs/8/clean-urls-in-drupal-8']),
+ ];
+ }
+
+ if (!$rewrite_error && !$rewrite_warning) {
+ $requirements['rewrite_module'] = [
+ 'title' => t('Clean URLs'),
+ 'value' => t('Disabled'),
+ 'severity' => RequirementSeverity::Warning,
+ 'description' => t('Your server is capable of using clean URLs, but it is not enabled. Using clean URLs gives an improved user experience and is recommended. <a href=":link">Enable clean URLs</a>', [':link' => 'https://www.drupal.org/docs/8/clean-urls-in-drupal-8']),
+ ];
+ }
+ }
+
+ // Verify the user is running a supported PHP version.
+ // If the site is running a recommended version of PHP, just display it
+ // as an informational message on the status report. This will be overridden
+ // with an error or warning if the site is running older PHP versions for
+ // which Drupal has already or will soon drop support.
+ $phpversion = $phpversion_label = phpversion();
+ if ($phase === 'runtime') {
+ $phpversion_label = t('@phpversion (<a href=":url">more information</a>)', [
+ '@phpversion' => $phpversion,
+ ':url' => (new Url('system.php'))->toString(),
+ ]);
+ }
+ $requirements['php'] = [
+ 'title' => t('PHP'),
+ 'value' => $phpversion_label,
+ ];
+
+ // Check if the PHP version is below what Drupal supports.
+ if (version_compare($phpversion, $minimum_supported_php) < 0) {
+ $requirements['php']['description'] = t('Your PHP installation is too old. Drupal requires at least PHP %version. It is recommended to upgrade to PHP version %recommended or higher for the best ongoing support. See <a href="http://php.net/supported-versions.php">PHP\'s version support documentation</a> and the <a href=":php_requirements">Drupal PHP requirements</a> page for more information.',
+ [
+ '%version' => $minimum_supported_php,
+ '%recommended' => \Drupal::RECOMMENDED_PHP,
+ ':php_requirements' => 'https://www.drupal.org/docs/system-requirements/php-requirements',
+ ]
+ );
+
+ // If the PHP version is also below the absolute minimum allowed, it's not
+ // safe to continue with the requirements check, and should always be an
+ // error.
+ if (version_compare($phpversion, \Drupal::MINIMUM_PHP) < 0) {
+ $requirements['php']['severity'] = RequirementSeverity::Error;
+ return $requirements;
+ }
+ // Otherwise, the message should be an error at runtime, and a warning
+ // during installation or update.
+ $requirements['php']['severity'] = ($phase === 'runtime') ? RequirementSeverity::Error : RequirementSeverity::Warning;
+ }
+ // For PHP versions that are still supported but no longer recommended,
+ // inform users of what's recommended, allowing them to take action before
+ // it becomes urgent.
+ elseif ($phase === 'runtime' && version_compare($phpversion, \Drupal::RECOMMENDED_PHP) < 0) {
+ $requirements['php']['description'] = t('It is recommended to upgrade to PHP version %recommended or higher for the best ongoing support. See <a href="http://php.net/supported-versions.php">PHP\'s version support documentation</a> and the <a href=":php_requirements">Drupal PHP requirements</a> page for more information.', ['%recommended' => \Drupal::RECOMMENDED_PHP, ':php_requirements' => 'https://www.drupal.org/docs/system-requirements/php-requirements']);
+ $requirements['php']['severity'] = RequirementSeverity::Info;
+ }
+
+ // Test for PHP extensions.
+ $requirements['php_extensions'] = [
+ 'title' => t('PHP extensions'),
+ ];
+
+ $missing_extensions = [];
+ $required_extensions = [
+ 'date',
+ 'dom',
+ 'filter',
+ 'gd',
+ 'hash',
+ 'json',
+ 'pcre',
+ 'pdo',
+ 'session',
+ 'SimpleXML',
+ 'SPL',
+ 'tokenizer',
+ 'xml',
+ 'zlib',
+ ];
+ foreach ($required_extensions as $extension) {
+ if (!extension_loaded($extension)) {
+ $missing_extensions[] = $extension;
+ }
+ }
+
+ if (!empty($missing_extensions)) {
+ $description = t('Drupal requires you to enable the PHP extensions in the following list (see the <a href=":system_requirements">system requirements page</a> for more information):', [
+ ':system_requirements' => 'https://www.drupal.org/docs/system-requirements',
+ ]);
+
+ // We use twig inline_template to avoid twig's autoescape.
+ $description = [
+ '#type' => 'inline_template',
+ '#template' => '{{ description }}{{ missing_extensions }}',
+ '#context' => [
+ 'description' => $description,
+ 'missing_extensions' => [
+ '#theme' => 'item_list',
+ '#items' => $missing_extensions,
+ ],
+ ],
+ ];
+
+ $requirements['php_extensions']['value'] = t('Disabled');
+ $requirements['php_extensions']['severity'] = RequirementSeverity::Error;
+ $requirements['php_extensions']['description'] = $description;
+ }
+ else {
+ $requirements['php_extensions']['value'] = t('Enabled');
+ }
+
+ if ($phase == 'install' || $phase == 'runtime') {
+ // Check to see if OPcache is installed.
+ if (!OpCodeCache::isEnabled()) {
+ $requirements['php_opcache'] = [
+ 'value' => t('Not enabled'),
+ 'severity' => RequirementSeverity::Warning,
+ 'description' => t('PHP OPcode caching can improve your site\'s performance considerably. It is <strong>highly recommended</strong> to have <a href="http://php.net/manual/opcache.installation.php" target="_blank">OPcache</a> installed on your server.'),
+ ];
+ }
+ else {
+ $requirements['php_opcache']['value'] = t('Enabled');
+ }
+ $requirements['php_opcache']['title'] = t('PHP OPcode caching');
+ }
+
+ // Check to see if APCu is installed and configured correctly.
+ if ($phase == 'runtime' && PHP_SAPI != 'cli') {
+ $requirements['php_apcu_enabled']['title'] = t('PHP APCu caching');
+ $requirements['php_apcu_available']['title'] = t('PHP APCu available caching');
+ if (extension_loaded('apcu') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN)) {
+ $memory_info = apcu_sma_info(TRUE);
+ $apcu_actual_size = ByteSizeMarkup::create($memory_info['seg_size'] * $memory_info['num_seg']);
+ $apcu_recommended_size = '32 MB';
+ $requirements['php_apcu_enabled']['value'] = t('Enabled (@size)', ['@size' => $apcu_actual_size]);
+ if (Bytes::toNumber(ini_get('apc.shm_size')) * ini_get('apc.shm_segments') < Bytes::toNumber($apcu_recommended_size)) {
+ $requirements['php_apcu_enabled']['severity'] = RequirementSeverity::Warning;
+ $requirements['php_apcu_enabled']['description'] = t('Depending on your configuration, Drupal can run with a @apcu_size APCu limit. However, a @apcu_default_size APCu limit (the default) or above is recommended, especially if your site uses additional custom or contributed modules.', [
+ '@apcu_size' => $apcu_actual_size,
+ '@apcu_default_size' => $apcu_recommended_size,
+ ]);
+ }
+ else {
+ $memory_available = $memory_info['avail_mem'] / ($memory_info['seg_size'] * $memory_info['num_seg']);
+ if ($memory_available < 0.1) {
+ $requirements['php_apcu_available']['severity'] = RequirementSeverity::Error;
+ $requirements['php_apcu_available']['description'] = t('APCu is using over 90% of its allotted memory (@apcu_actual_size). To improve APCu performance, consider increasing this limit.', [
+ '@apcu_actual_size' => $apcu_actual_size,
+ ]);
+ }
+ elseif ($memory_available < 0.25) {
+ $requirements['php_apcu_available']['severity'] = RequirementSeverity::Warning;
+ $requirements['php_apcu_available']['description'] = t('APCu is using over 75% of its allotted memory (@apcu_actual_size). To improve APCu performance, consider increasing this limit.', [
+ '@apcu_actual_size' => $apcu_actual_size,
+ ]);
+ }
+ else {
+ $requirements['php_apcu_available']['severity'] = RequirementSeverity::OK;
+ }
+ $requirements['php_apcu_available']['value'] = t('Memory available: @available.', [
+ '@available' => ByteSizeMarkup::create($memory_info['avail_mem']),
+ ]);
+ }
+ }
+ else {
+ $requirements['php_apcu_enabled'] += [
+ 'value' => t('Not enabled'),
+ 'severity' => RequirementSeverity::Info,
+ 'description' => t('PHP APCu caching can improve your site\'s performance considerably. It is <strong>highly recommended</strong> to have <a href="https://www.php.net/manual/apcu.installation.php" target="_blank">APCu</a> installed on your server.'),
+ ];
+ }
+ }
+
+ if ($phase != 'update') {
+ // Test whether we have a good source of random bytes.
+ $requirements['php_random_bytes'] = [
+ 'title' => t('Random number generation'),
+ ];
+ try {
+ $bytes = random_bytes(10);
+ if (strlen($bytes) != 10) {
+ throw new \Exception("Tried to generate 10 random bytes, generated '" . strlen($bytes) . "'");
+ }
+ $requirements['php_random_bytes']['value'] = t('Successful');
+ }
+ catch (\Exception $e) {
+ // If /dev/urandom is not available on a UNIX-like system, check whether
+ // open_basedir restrictions are the cause.
+ $open_basedir_blocks_urandom = FALSE;
+ if (DIRECTORY_SEPARATOR === '/' && !@is_readable('/dev/urandom')) {
+ $open_basedir = ini_get('open_basedir');
+ if ($open_basedir) {
+ $open_basedir_paths = explode(PATH_SEPARATOR, $open_basedir);
+ $open_basedir_blocks_urandom = !array_intersect(['/dev', '/dev/', '/dev/urandom'], $open_basedir_paths);
+ }
+ }
+ $args = [
+ ':drupal-php' => 'https://www.drupal.org/docs/system-requirements/php-requirements',
+ '%exception_message' => $e->getMessage(),
+ ];
+ if ($open_basedir_blocks_urandom) {
+ $requirements['php_random_bytes']['description'] = t('Drupal is unable to generate highly randomized numbers, which means certain security features like password reset URLs are not as secure as they should be. Instead, only a slow, less-secure fallback generator is available. The most likely cause is that open_basedir restrictions are in effect and /dev/urandom is not on the allowed list. See the <a href=":drupal-php">system requirements</a> page for more information. %exception_message', $args);
+ }
+ else {
+ $requirements['php_random_bytes']['description'] = t('Drupal is unable to generate highly randomized numbers, which means certain security features like password reset URLs are not as secure as they should be. Instead, only a slow, less-secure fallback generator is available. See the <a href=":drupal-php">system requirements</a> page for more information. %exception_message', $args);
+ }
+ $requirements['php_random_bytes']['value'] = t('Less secure');
+ $requirements['php_random_bytes']['severity'] = RequirementSeverity::Error;
+ }
+ }
+
+ if ($phase === 'runtime' && PHP_SAPI !== 'cli') {
+ if (!function_exists('fastcgi_finish_request') && !function_exists('litespeed_finish_request') && !ob_get_status()) {
+ $requirements['output_buffering'] = [
+ 'title' => t('Output Buffering'),
+ 'error_value' => t('Not enabled'),
+ 'severity' => RequirementSeverity::Warning,
+ 'description' => t('<a href="https://www.php.net/manual/en/function.ob-start.php">Output buffering</a> is not enabled. This may degrade Drupal\'s performance. You can enable output buffering by default <a href="https://www.php.net/manual/en/outcontrol.configuration.php#ini.output-buffering">in your PHP settings</a>.'),
+ ];
+ }
+ }
+
+ if ($phase == 'install' || $phase == 'update') {
+ // Test for PDO (database).
+ $requirements['database_extensions'] = [
+ 'title' => t('Database support'),
+ ];
+
+ // Make sure PDO is available.
+ $database_ok = extension_loaded('pdo');
+ if (!$database_ok) {
+ $pdo_message = t('Your web server does not appear to support PDO (PHP Data Objects). Ask your hosting provider if they support the native PDO extension. See the <a href=":link">system requirements</a> page for more information.', [
+ ':link' => 'https://www.drupal.org/docs/system-requirements/php-requirements#database',
+ ]);
+ }
+ else {
+ // Make sure at least one supported database driver exists.
+ if (empty(Database::getDriverList()->getInstallableList())) {
+ $database_ok = FALSE;
+ $pdo_message = t('Your web server does not appear to support any common PDO database extensions. Check with your hosting provider to see if they support PDO (PHP Data Objects) and offer any databases that <a href=":drupal-databases">Drupal supports</a>.', [
+ ':drupal-databases' => 'https://www.drupal.org/docs/system-requirements/database-server-requirements',
+ ]);
+ }
+ // Make sure the native PDO extension is available, not the older PEAR
+ // version. (See install_verify_pdo() for details.)
+ if (!defined('PDO::ATTR_DEFAULT_FETCH_MODE')) {
+ $database_ok = FALSE;
+ $pdo_message = t('Your web server seems to have the wrong version of PDO installed. Drupal requires the PDO extension from PHP core. This system has the older PECL version. See the <a href=":link">system requirements</a> page for more information.', [
+ ':link' => 'https://www.drupal.org/docs/system-requirements/php-requirements#database',
+ ]);
+ }
+ }
+
+ if (!$database_ok) {
+ $requirements['database_extensions']['value'] = t('Disabled');
+ $requirements['database_extensions']['severity'] = RequirementSeverity::Error;
+ $requirements['database_extensions']['description'] = $pdo_message;
+ }
+ else {
+ $requirements['database_extensions']['value'] = t('Enabled');
+ }
+ }
+
+ if ($phase === 'runtime' || $phase === 'update') {
+ // Database information.
+ $class = Database::getConnection()->getConnectionOptions()['namespace'] . '\\Install\\Tasks';
+ /** @var \Drupal\Core\Database\Install\Tasks $tasks */
+ $tasks = new $class();
+ $requirements['database_system'] = [
+ 'title' => t('Database system'),
+ 'value' => $tasks->name(),
+ ];
+ $requirements['database_system_version'] = [
+ 'title' => t('Database system version'),
+ 'value' => Database::getConnection()->version(),
+ ];
+
+ $errors = $tasks->engineVersionRequirementsCheck();
+ $error_count = count($errors);
+ if ($error_count > 0) {
+ $error_message = [
+ '#theme' => 'item_list',
+ '#items' => $errors,
+ // Use the comma-list style to display a single error without bullets.
+ '#context' => ['list_style' => $error_count === 1 ? 'comma-list' : ''],
+ ];
+ $requirements['database_system_version']['severity'] = RequirementSeverity::Error;
+ $requirements['database_system_version']['description'] = $error_message;
+ }
+ }
+
+ if ($phase === 'runtime' || $phase === 'update') {
+ // Test database JSON support.
+ $requirements['database_support_json'] = [
+ 'title' => t('Database support for JSON'),
+ 'severity' => RequirementSeverity::OK,
+ 'value' => t('Available'),
+ 'description' => t('Drupal requires databases that support JSON storage.'),
+ ];
+
+ if (!Database::getConnection()->hasJson()) {
+ $requirements['database_support_json']['value'] = t('Not available');
+ $requirements['database_support_json']['severity'] = RequirementSeverity::Error;
+ }
+ }
+
+ // Test PHP memory_limit
+ $memory_limit = ini_get('memory_limit');
+ $requirements['php_memory_limit'] = [
+ 'title' => t('PHP memory limit'),
+ 'value' => $memory_limit == -1 ? t('-1 (Unlimited)') : $memory_limit,
+ ];
+
+ if (!Environment::checkMemoryLimit(\Drupal::MINIMUM_PHP_MEMORY_LIMIT, $memory_limit)) {
+ $description = [];
+ if ($phase == 'install') {
+ $description['phase'] = t('Consider increasing your PHP memory limit to %memory_minimum_limit to help prevent errors in the installation process.', ['%memory_minimum_limit' => \Drupal::MINIMUM_PHP_MEMORY_LIMIT]);
+ }
+ elseif ($phase == 'update') {
+ $description['phase'] = t('Consider increasing your PHP memory limit to %memory_minimum_limit to help prevent errors in the update process.', ['%memory_minimum_limit' => \Drupal::MINIMUM_PHP_MEMORY_LIMIT]);
+ }
+ elseif ($phase == 'runtime') {
+ $description['phase'] = t('Depending on your configuration, Drupal can run with a %memory_limit PHP memory limit. However, a %memory_minimum_limit PHP memory limit or above is recommended, especially if your site uses additional custom or contributed modules.', ['%memory_limit' => $memory_limit, '%memory_minimum_limit' => \Drupal::MINIMUM_PHP_MEMORY_LIMIT]);
+ }
+
+ if (!empty($description['phase'])) {
+ if ($php_ini_path = get_cfg_var('cfg_file_path')) {
+ $description['memory'] = t('Increase the memory limit by editing the memory_limit parameter in the file %configuration-file and then restart your web server (or contact your system administrator or hosting provider for assistance).', ['%configuration-file' => $php_ini_path]);
+ }
+ else {
+ $description['memory'] = t('Contact your system administrator or hosting provider for assistance with increasing your PHP memory limit.');
+ }
+
+ $handbook_link = t('For more information, see the online handbook entry for <a href=":memory-limit">increasing the PHP memory limit</a>.', [':memory-limit' => 'https://www.drupal.org/node/207036']);
+
+ $description = [
+ '#type' => 'inline_template',
+ '#template' => '{{ description_phase }} {{ description_memory }} {{ handbook }}',
+ '#context' => [
+ 'description_phase' => $description['phase'],
+ 'description_memory' => $description['memory'],
+ 'handbook' => $handbook_link,
+ ],
+ ];
+
+ $requirements['php_memory_limit']['description'] = $description;
+ $requirements['php_memory_limit']['severity'] = RequirementSeverity::Warning;
+ }
+ }
+
+ // Test if configuration files and directory are writable.
+ if ($phase == 'runtime') {
+ $conf_errors = [];
+ // Find the site path. Kernel service is not always available at this
+ // point, but is preferred, when available.
+ if (\Drupal::hasService('kernel')) {
+ $site_path = \Drupal::getContainer()->getParameter('site.path');
+ }
+ else {
+ $site_path = DrupalKernel::findSitePath(Request::createFromGlobals());
+ }
+ // Allow system administrators to disable permissions hardening for the
+ // site directory. This allows additional files in the site directory to
+ // be updated when they are managed in a version control system.
+ if (Settings::get('skip_permissions_hardening')) {
+ $error_value = t('Protection disabled');
+ // If permissions hardening is disabled, then only show a warning for a
+ // writable file, as a reminder, rather than an error.
+ $file_protection_severity = RequirementSeverity::Warning;
+ }
+ else {
+ $error_value = t('Not protected');
+ // In normal operation, writable files or directories are an error.
+ $file_protection_severity = RequirementSeverity::Error;
+ if (!drupal_verify_install_file($site_path, FILE_NOT_WRITABLE, 'dir')) {
+ $conf_errors[] = t("The directory %file is not protected from modifications and poses a security risk. You must change the directory's permissions to be non-writable.", ['%file' => $site_path]);
+ }
+ }
+ foreach (['settings.php', 'settings.local.php', 'services.yml'] as $conf_file) {
+ $full_path = $site_path . '/' . $conf_file;
+ if (file_exists($full_path) && !drupal_verify_install_file($full_path, FILE_EXIST | FILE_READABLE | FILE_NOT_WRITABLE, 'file', !Settings::get('skip_permissions_hardening'))) {
+ $conf_errors[] = t("The file %file is not protected from modifications and poses a security risk. You must change the file's permissions to be non-writable.", ['%file' => $full_path]);
+ }
+ }
+ if (!empty($conf_errors)) {
+ if (count($conf_errors) == 1) {
+ $description = $conf_errors[0];
+ }
+ else {
+ // We use twig inline_template to avoid double escaping.
+ $description = [
+ '#type' => 'inline_template',
+ '#template' => '{{ configuration_error_list }}',
+ '#context' => [
+ 'configuration_error_list' => [
+ '#theme' => 'item_list',
+ '#items' => $conf_errors,
+ ],
+ ],
+ ];
+ }
+ $requirements['configuration_files'] = [
+ 'value' => $error_value,
+ 'severity' => $file_protection_severity,
+ 'description' => $description,
+ ];
+ }
+ else {
+ $requirements['configuration_files'] = [
+ 'value' => t('Protected'),
+ ];
+ }
+ $requirements['configuration_files']['title'] = t('Configuration files');
+ }
+
+ // Test the contents of the .htaccess files.
+ if ($phase == 'runtime' && Settings::get('auto_create_htaccess', TRUE)) {
+ // Try to write the .htaccess files first, to prevent false alarms in
+ // case (for example) the /tmp directory was wiped.
+ /** @var \Drupal\Core\File\HtaccessWriterInterface $htaccessWriter */
+ $htaccessWriter = \Drupal::service("file.htaccess_writer");
+ $htaccessWriter->ensure();
+ foreach ($htaccessWriter->defaultProtectedDirs() as $protected_dir) {
+ $htaccess_file = $protected_dir->getPath() . '/.htaccess';
+ // Check for the string which was added to the recommended .htaccess
+ // file in the latest security update.
+ if (!file_exists($htaccess_file) || !($contents = @file_get_contents($htaccess_file)) || !str_contains($contents, 'Drupal_Security_Do_Not_Remove_See_SA_2013_003')) {
+ $url = 'https://www.drupal.org/SA-CORE-2013-003';
+ $requirements[$htaccess_file] = [
+ // phpcs:ignore Drupal.Semantics.FunctionT.NotLiteralString
+ 'title' => new TranslatableMarkup($protected_dir->getTitle()),
+ 'value' => t('Not fully protected'),
+ 'severity' => RequirementSeverity::Error,
+ 'description' => t('See <a href=":url">@url</a> for information about the recommended .htaccess file which should be added to the %directory directory to help protect against arbitrary code execution.', [':url' => $url, '@url' => $url, '%directory' => $protected_dir->getPath()]),
+ ];
+ }
+ }
+ }
+
+ // Report cron status.
+ if ($phase == 'runtime') {
+ $cron_config = \Drupal::config('system.cron');
+ // Cron warning threshold defaults to two days.
+ $threshold_warning = $cron_config->get('threshold.requirements_warning');
+ // Cron error threshold defaults to two weeks.
+ $threshold_error = $cron_config->get('threshold.requirements_error');
+
+ // Determine when cron last ran.
+ $cron_last = \Drupal::state()->get('system.cron_last');
+ if (!is_numeric($cron_last)) {
+ $cron_last = \Drupal::state()->get('install_time', 0);
+ }
+
+ // Determine severity based on time since cron last ran.
+ $severity = RequirementSeverity::Info;
+ $request_time = \Drupal::time()->getRequestTime();
+ if ($request_time - $cron_last > $threshold_error) {
+ $severity = RequirementSeverity::Error;
+ }
+ elseif ($request_time - $cron_last > $threshold_warning) {
+ $severity = RequirementSeverity::Warning;
+ }
+
+ // Set summary and description based on values determined above.
+ $summary = t('Last run @time ago', ['@time' => \Drupal::service('date.formatter')->formatTimeDiffSince($cron_last)]);
+
+ $requirements['cron'] = [
+ 'title' => t('Cron maintenance tasks'),
+ 'severity' => $severity,
+ 'value' => $summary,
+ ];
+ if ($severity != RequirementSeverity::Info) {
+ $requirements['cron']['description'][] = [
+ [
+ '#markup' => t('Cron has not run recently.'),
+ '#suffix' => ' ',
+ ],
+ [
+ '#markup' => t('For more information, see the online handbook entry for <a href=":cron-handbook">configuring cron jobs</a>.', [':cron-handbook' => 'https://www.drupal.org/docs/administering-a-drupal-site/cron-automated-tasks/cron-automated-tasks-overview']),
+ '#suffix' => ' ',
+ ],
+ ];
+ }
+ $requirements['cron']['description'][] = [
+ [
+ '#type' => 'link',
+ '#prefix' => '(',
+ '#title' => t('more information'),
+ '#suffix' => ')',
+ '#url' => Url::fromRoute('system.cron_settings'),
+ ],
+ [
+ '#prefix' => '<span class="cron-description__run-cron">',
+ '#suffix' => '</span>',
+ '#type' => 'link',
+ '#title' => t('Run cron'),
+ '#url' => Url::fromRoute('system.run_cron'),
+ ],
+ ];
+ }
+ if ($phase != 'install') {
+ $directories = [
+ PublicStream::basePath(),
+ // By default no private files directory is configured. For private
+ // files to be secure the admin needs to provide a path outside the
+ // webroot.
+ PrivateStream::basePath(),
+ \Drupal::service('file_system')->getTempDirectory(),
+ ];
+ }
+
+ // During an install we need to make assumptions about the file system
+ // unless overrides are provided in settings.php.
+ if ($phase == 'install') {
+ $directories = [];
+ if ($file_public_path = Settings::get('file_public_path')) {
+ $directories[] = $file_public_path;
+ }
+ else {
+ // If we are installing Drupal, the settings.php file might not exist
+ // yet in the intended site directory, so don't require it.
+ $request = Request::createFromGlobals();
+ $site_path = DrupalKernel::findSitePath($request);
+ $directories[] = $site_path . '/files';
+ }
+ if ($file_private_path = Settings::get('file_private_path')) {
+ $directories[] = $file_private_path;
+ }
+ if (Settings::get('file_temp_path')) {
+ $directories[] = Settings::get('file_temp_path');
+ }
+ else {
+ // If the temporary directory is not overridden use an appropriate
+ // temporary path for the system.
+ $directories[] = FileSystemComponent::getOsTemporaryDirectory();
+ }
+ }
+
+ // Check the config directory if it is defined in settings.php. If it isn't
+ // defined, the installer will create a valid config directory later, but
+ // during runtime we must always display an error.
+ $config_sync_directory = Settings::get('config_sync_directory');
+ if (!empty($config_sync_directory)) {
+ // If we're installing Drupal try and create the config sync directory.
+ if (!is_dir($config_sync_directory) && $phase == 'install') {
+ \Drupal::service('file_system')->prepareDirectory($config_sync_directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
+ }
+ if (!is_dir($config_sync_directory)) {
+ if ($phase == 'install') {
+ $description = t('An automated attempt to create the directory %directory failed, possibly due to a permissions problem. To proceed with the installation, either create the directory and modify its permissions manually or ensure that the installer has the permissions to create it automatically. For more information, see INSTALL.txt or the <a href=":handbook_url">online handbook</a>.', ['%directory' => $config_sync_directory, ':handbook_url' => 'https://www.drupal.org/server-permissions']);
+ }
+ else {
+ $description = t('The directory %directory does not exist.', ['%directory' => $config_sync_directory]);
+ }
+ $requirements['config sync directory'] = [
+ 'title' => t('Configuration sync directory'),
+ 'description' => $description,
+ 'severity' => RequirementSeverity::Error,
+ ];
+ }
+ }
+ if ($phase != 'install' && empty($config_sync_directory)) {
+ $requirements['config sync directory'] = [
+ 'title' => t('Configuration sync directory'),
+ 'value' => t('Not present'),
+ 'description' => t("Your %file file must define the %setting setting as a string containing the directory in which configuration files can be found.", ['%file' => $site_path . '/settings.php', '%setting' => "\$settings['config_sync_directory']"]),
+ 'severity' => RequirementSeverity::Error,
+ ];
+ }
+
+ $requirements['file system'] = [
+ 'title' => t('File system'),
+ ];
+
+ $error = '';
+ // For installer, create the directories if possible.
+ foreach ($directories as $directory) {
+ if (!$directory) {
+ continue;
+ }
+ if ($phase == 'install') {
+ \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
+ }
+ $is_writable = is_writable($directory);
+ $is_directory = is_dir($directory);
+ if (!$is_writable || !$is_directory) {
+ $description = '';
+ $requirements['file system']['value'] = t('Not writable');
+ if (!$is_directory) {
+ $error = t('The directory %directory does not exist.', ['%directory' => $directory]);
+ }
+ else {
+ $error = t('The directory %directory is not writable.', ['%directory' => $directory]);
+ }
+ // The files directory requirement check is done only during install and
+ // runtime.
+ if ($phase == 'runtime') {
+ $description = t('You may need to set the correct directory at the <a href=":admin-file-system">file system settings page</a> or change the current directory\'s permissions so that it is writable.', [':admin-file-system' => Url::fromRoute('system.file_system_settings')->toString()]);
+ }
+ elseif ($phase == 'install') {
+ // For the installer UI, we need different wording. 'value' will
+ // be treated as version, so provide none there.
+ $description = t('An automated attempt to create this directory failed, possibly due to a permissions problem. To proceed with the installation, either create the directory and modify its permissions manually or ensure that the installer has the permissions to create it automatically. For more information, see INSTALL.txt or the <a href=":handbook_url">online handbook</a>.', [':handbook_url' => 'https://www.drupal.org/server-permissions']);
+ $requirements['file system']['value'] = '';
+ }
+ if (!empty($description)) {
+ $description = [
+ '#type' => 'inline_template',
+ '#template' => '{{ error }} {{ description }}',
+ '#context' => [
+ 'error' => $error,
+ 'description' => $description,
+ ],
+ ];
+ $requirements['file system']['description'] = $description;
+ $requirements['file system']['severity'] = RequirementSeverity::Error;
+ }
+ }
+ else {
+ // This function can be called before the config_cache table has been
+ // created.
+ if ($phase == 'install' || \Drupal::config('system.file')->get('default_scheme') == 'public') {
+ $requirements['file system']['value'] = t('Writable (<em>public</em> download method)');
+ }
+ else {
+ $requirements['file system']['value'] = t('Writable (<em>private</em> download method)');
+ }
+ }
+ }
+
+ // See if updates are available in update.php.
+ if ($phase == 'runtime') {
+ $requirements['update'] = [
+ 'title' => t('Database updates'),
+ 'value' => t('Up to date'),
+ ];
+
+ // Check installed modules.
+ $has_pending_updates = FALSE;
+ /** @var \Drupal\Core\Update\UpdateHookRegistry $update_registry */
+ $update_registry = \Drupal::service('update.update_hook_registry');
+ foreach (\Drupal::moduleHandler()->getModuleList() as $module => $filename) {
+ $updates = $update_registry->getAvailableUpdates($module);
+ if ($updates) {
+ $default = $update_registry->getInstalledVersion($module);
+ if (max($updates) > $default) {
+ $has_pending_updates = TRUE;
+ break;
+ }
+ }
+ }
+ if (!$has_pending_updates) {
+ /** @var \Drupal\Core\Update\UpdateRegistry $post_update_registry */
+ $post_update_registry = \Drupal::service('update.post_update_registry');
+ $missing_post_update_functions = $post_update_registry->getPendingUpdateFunctions();
+ if (!empty($missing_post_update_functions)) {
+ $has_pending_updates = TRUE;
+ }
+ }
+
+ if ($has_pending_updates) {
+ $requirements['update']['severity'] = RequirementSeverity::Error;
+ $requirements['update']['value'] = t('Out of date');
+ $requirements['update']['description'] = t('Some modules have database schema updates to install. You should run the <a href=":update">database update script</a> immediately.', [':update' => Url::fromRoute('system.db_update')->toString()]);
+ }
+
+ $requirements['entity_update'] = [
+ 'title' => t('Entity/field definitions'),
+ 'value' => t('Up to date'),
+ ];
+ // Verify that no entity updates are pending.
+ if ($change_list = \Drupal::entityDefinitionUpdateManager()->getChangeSummary()) {
+ $build = [];
+ foreach ($change_list as $entity_type_id => $changes) {
+ $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
+ $build[] = [
+ '#theme' => 'item_list',
+ '#title' => $entity_type->getLabel(),
+ '#items' => $changes,
+ ];
+ }
+
+ $entity_update_issues = \Drupal::service('renderer')->renderInIsolation($build);
+ $requirements['entity_update']['severity'] = RequirementSeverity::Error;
+ $requirements['entity_update']['value'] = t('Mismatched entity and/or field definitions');
+ $requirements['entity_update']['description'] = t('The following changes were detected in the entity type and field definitions. @updates', ['@updates' => $entity_update_issues]);
+ }
+ }
+
+ // Display the deployment identifier if set.
+ if ($phase == 'runtime') {
+ if ($deployment_identifier = Settings::get('deployment_identifier')) {
+ $requirements['deployment identifier'] = [
+ 'title' => t('Deployment identifier'),
+ 'value' => $deployment_identifier,
+ 'severity' => RequirementSeverity::Info,
+ ];
+ }
+ }
+
+ // Verify the update.php access setting
+ if ($phase == 'runtime') {
+ if (Settings::get('update_free_access')) {
+ $requirements['update access'] = [
+ 'value' => t('Not protected'),
+ 'severity' => RequirementSeverity::Error,
+ 'description' => t('The update.php script is accessible to everyone without authentication check, which is a security risk. You must change the @settings_name value in your settings.php back to FALSE.', ['@settings_name' => '$settings[\'update_free_access\']']),
+ ];
+ }
+ else {
+ $requirements['update access'] = [
+ 'value' => t('Protected'),
+ ];
+ }
+ $requirements['update access']['title'] = t('Access to update.php');
+ }
+
+ // Display an error if a newly introduced dependency in a module is not
+ // resolved.
+ if ($phase === 'update' || $phase === 'runtime') {
+ $create_extension_incompatibility_list = function (array $extension_names, PluralTranslatableMarkup $description, PluralTranslatableMarkup $title, TranslatableMarkup|string $message = '', TranslatableMarkup|string $additional_description = '') {
+ if ($message === '') {
+ $message = new TranslatableMarkup('Review the <a href=":url"> suggestions for resolving this incompatibility</a> to repair your installation, and then re-run update.php.', [':url' => 'https://www.drupal.org/docs/updating-drupal/troubleshooting-database-updates']);
+ }
+ // Use an inline twig template to:
+ // - Concatenate MarkupInterface objects and preserve safeness.
+ // - Use the item_list theme for the extension list.
+ $template = [
+ '#type' => 'inline_template',
+ '#template' => '{{ description }}{{ extensions }}{{ additional_description }}<br>',
+ '#context' => [
+ 'extensions' => [
+ '#theme' => 'item_list',
+ ],
+ ],
+ ];
+ $template['#context']['extensions']['#items'] = $extension_names;
+ $template['#context']['description'] = $description;
+ $template['#context']['additional_description'] = $additional_description;
+ return [
+ 'title' => $title,
+ 'value' => [
+ 'list' => $template,
+ 'handbook_link' => [
+ '#markup' => $message,
+ ],
+ ],
+ 'severity' => RequirementSeverity::Error,
+ ];
+ };
+ $profile = \Drupal::installProfile();
+ $files = $module_extension_list->getList();
+ $files += $theme_extension_list->getList();
+ $core_incompatible_extensions = [];
+ $php_incompatible_extensions = [];
+ foreach ($files as $extension_name => $file) {
+ // Ignore uninstalled extensions and installation profiles.
+ if (!$file->status || $extension_name == $profile) {
+ continue;
+ }
+
+ $name = $file->info['name'];
+ if (!empty($file->info['core_incompatible'])) {
+ $core_incompatible_extensions[$file->info['type']][] = $name;
+ }
+
+ // Check the extension's PHP version.
+ $php = (string) $file->info['php'];
+ if (version_compare($php, PHP_VERSION, '>')) {
+ $php_incompatible_extensions[$file->info['type']][] = $name;
+ }
+
+ // Check the module's required modules.
+ /** @var \Drupal\Core\Extension\Dependency $requirement */
+ foreach ($file->requires as $requirement) {
+ $required_module = $requirement->getName();
+ // Check if the module exists.
+ if (!isset($files[$required_module])) {
+ $requirements["$extension_name-$required_module"] = [
+ 'title' => t('Unresolved dependency'),
+ 'description' => t('@name requires this module.', ['@name' => $name]),
+ 'value' => t('@required_name (Missing)', ['@required_name' => $required_module]),
+ 'severity' => RequirementSeverity::Error,
+ ];
+ continue;
+ }
+ // Check for an incompatible version.
+ $required_file = $files[$required_module];
+ $required_name = $required_file->info['name'];
+ // Remove CORE_COMPATIBILITY- only from the start of the string.
+ $version = preg_replace('/^(' . \Drupal::CORE_COMPATIBILITY . '\-)/', '', $required_file->info['version'] ?? '');
+ if (!$requirement->isCompatible($version)) {
+ $requirements["$extension_name-$required_module"] = [
+ 'title' => t('Unresolved dependency'),
+ 'description' => t('@name requires this module and version. Currently using @required_name version @version', ['@name' => $name, '@required_name' => $required_name, '@version' => $version]),
+ 'value' => t('@required_name (Version @compatibility required)', ['@required_name' => $required_name, '@compatibility' => $requirement->getConstraintString()]),
+ 'severity' => RequirementSeverity::Error,
+ ];
+ continue;
+ }
+ }
+ }
+ if (!empty($core_incompatible_extensions['module'])) {
+ $requirements['module_core_incompatible'] = $create_extension_incompatibility_list(
+ $core_incompatible_extensions['module'],
+ new PluralTranslatableMarkup(
+ count($core_incompatible_extensions['module']),
+ 'The following module is installed, but it is incompatible with Drupal @version:',
+ 'The following modules are installed, but they are incompatible with Drupal @version:',
+ ['@version' => \Drupal::VERSION]
+ ),
+ new PluralTranslatableMarkup(
+ count($core_incompatible_extensions['module']),
+ 'Incompatible module',
+ 'Incompatible modules'
+ )
+ );
+ }
+ if (!empty($core_incompatible_extensions['theme'])) {
+ $requirements['theme_core_incompatible'] = $create_extension_incompatibility_list(
+ $core_incompatible_extensions['theme'],
+ new PluralTranslatableMarkup(
+ count($core_incompatible_extensions['theme']),
+ 'The following theme is installed, but it is incompatible with Drupal @version:',
+ 'The following themes are installed, but they are incompatible with Drupal @version:',
+ ['@version' => \Drupal::VERSION]
+ ),
+ new PluralTranslatableMarkup(
+ count($core_incompatible_extensions['theme']),
+ 'Incompatible theme',
+ 'Incompatible themes'
+ )
+ );
+ }
+ if (!empty($php_incompatible_extensions['module'])) {
+ $requirements['module_php_incompatible'] = $create_extension_incompatibility_list(
+ $php_incompatible_extensions['module'],
+ new PluralTranslatableMarkup(
+ count($php_incompatible_extensions['module']),
+ 'The following module is installed, but it is incompatible with PHP @version:',
+ 'The following modules are installed, but they are incompatible with PHP @version:',
+ ['@version' => phpversion()]
+ ),
+ new PluralTranslatableMarkup(
+ count($php_incompatible_extensions['module']),
+ 'Incompatible module',
+ 'Incompatible modules'
+ )
+ );
+ }
+ if (!empty($php_incompatible_extensions['theme'])) {
+ $requirements['theme_php_incompatible'] = $create_extension_incompatibility_list(
+ $php_incompatible_extensions['theme'],
+ new PluralTranslatableMarkup(
+ count($php_incompatible_extensions['theme']),
+ 'The following theme is installed, but it is incompatible with PHP @version:',
+ 'The following themes are installed, but they are incompatible with PHP @version:',
+ ['@version' => phpversion()]
+ ),
+ new PluralTranslatableMarkup(
+ count($php_incompatible_extensions['theme']),
+ 'Incompatible theme',
+ 'Incompatible themes'
+ )
+ );
+ }
+
+ $extension_config = \Drupal::configFactory()->get('core.extension');
+
+ // Look for removed core modules.
+ $is_removed_module = function ($extension_name) use ($module_extension_list) {
+ return !$module_extension_list->exists($extension_name)
+ && array_key_exists($extension_name, DRUPAL_CORE_REMOVED_MODULE_LIST);
+ };
+ $removed_modules = array_filter(array_keys($extension_config->get('module')), $is_removed_module);
+ if (!empty($removed_modules)) {
+ $list = [];
+ foreach ($removed_modules as $removed_module) {
+ $list[] = t('<a href=":url">@module</a>', [
+ ':url' => "https://www.drupal.org/project/$removed_module",
+ '@module' => DRUPAL_CORE_REMOVED_MODULE_LIST[$removed_module],
+ ]);
+ }
+ $requirements['removed_module'] = $create_extension_incompatibility_list(
+ $list,
+ new PluralTranslatableMarkup(
+ count($removed_modules),
+ 'You must add the following contributed module and reload this page.',
+ 'You must add the following contributed modules and reload this page.'
+ ),
+ new PluralTranslatableMarkup(
+ count($removed_modules),
+ 'Removed core module',
+ 'Removed core modules'
+ ),
+ new TranslatableMarkup(
+ 'For more information read the <a href=":url">documentation on deprecated modules.</a>',
+ [':url' => 'https://www.drupal.org/node/3223395#s-recommendations-for-deprecated-modules']
+ ),
+ new PluralTranslatableMarkup(
+ count($removed_modules),
+ 'This module is installed on your site but is no longer provided by Core.',
+ 'These modules are installed on your site but are no longer provided by Core.'
+ ),
+ );
+ }
+
+ // Look for removed core themes.
+ $is_removed_theme = function ($extension_name) use ($theme_extension_list) {
+ return !$theme_extension_list->exists($extension_name)
+ && array_key_exists($extension_name, DRUPAL_CORE_REMOVED_THEME_LIST);
+ };
+ $removed_themes = array_filter(array_keys($extension_config->get('theme')), $is_removed_theme);
+ if (!empty($removed_themes)) {
+ $list = [];
+ foreach ($removed_themes as $removed_theme) {
+ $list[] = t('<a href=":url">@theme</a>', [
+ ':url' => "https://www.drupal.org/project/$removed_theme",
+ '@theme' => DRUPAL_CORE_REMOVED_THEME_LIST[$removed_theme],
+ ]);
+ }
+ $requirements['removed_theme'] = $create_extension_incompatibility_list(
+ $list,
+ new PluralTranslatableMarkup(
+ count($removed_themes),
+ 'You must add the following contributed theme and reload this page.',
+ 'You must add the following contributed themes and reload this page.'
+ ),
+ new PluralTranslatableMarkup(
+ count($removed_themes),
+ 'Removed core theme',
+ 'Removed core themes'
+ ),
+ new TranslatableMarkup(
+ 'For more information read the <a href=":url">documentation on deprecated themes.</a>',
+ [':url' => 'https://www.drupal.org/node/3223395#s-recommendations-for-deprecated-themes']
+ ),
+ new PluralTranslatableMarkup(
+ count($removed_themes),
+ 'This theme is installed on your site but is no longer provided by Core.',
+ 'These themes are installed on your site but are no longer provided by Core.'
+ ),
+ );
+ }
+
+ // Look for missing modules.
+ $is_missing_module = function ($extension_name) use ($module_extension_list) {
+ return !$module_extension_list->exists($extension_name) && !in_array($extension_name, array_keys(DRUPAL_CORE_REMOVED_MODULE_LIST), TRUE);
+ };
+ $invalid_modules = array_filter(array_keys($extension_config->get('module')), $is_missing_module);
+
+ if (!empty($invalid_modules)) {
+ $requirements['invalid_module'] = $create_extension_incompatibility_list(
+ $invalid_modules,
+ new PluralTranslatableMarkup(
+ count($invalid_modules),
+ 'The following module is marked as installed in the core.extension configuration, but it is missing:',
+ 'The following modules are marked as installed in the core.extension configuration, but they are missing:'
+ ),
+ new PluralTranslatableMarkup(
+ count($invalid_modules),
+ 'Missing or invalid module',
+ 'Missing or invalid modules'
+ )
+ );
+ }
+
+ // Look for invalid themes.
+ $is_missing_theme = function ($extension_name) use (&$theme_extension_list) {
+ return !$theme_extension_list->exists($extension_name) && !in_array($extension_name, array_keys(DRUPAL_CORE_REMOVED_THEME_LIST), TRUE);
+ };
+ $invalid_themes = array_filter(array_keys($extension_config->get('theme')), $is_missing_theme);
+ if (!empty($invalid_themes)) {
+ $requirements['invalid_theme'] = $create_extension_incompatibility_list(
+ $invalid_themes,
+ new PluralTranslatableMarkup(
+ count($invalid_themes),
+ 'The following theme is marked as installed in the core.extension configuration, but it is missing:',
+ 'The following themes are marked as installed in the core.extension configuration, but they are missing:'
+ ),
+ new PluralTranslatableMarkup(
+ count($invalid_themes),
+ 'Missing or invalid theme',
+ 'Missing or invalid themes'
+ )
+ );
+ }
+ }
+
+ // Returns Unicode library status and errors.
+ $libraries = [
+ Unicode::STATUS_SINGLEBYTE => t('Standard PHP'),
+ Unicode::STATUS_MULTIBYTE => t('PHP Mbstring Extension'),
+ Unicode::STATUS_ERROR => t('Error'),
+ ];
+ $severities = [
+ Unicode::STATUS_SINGLEBYTE => RequirementSeverity::Warning,
+ Unicode::STATUS_MULTIBYTE => NULL,
+ Unicode::STATUS_ERROR => RequirementSeverity::Error,
+ ];
+ $failed_check = Unicode::check();
+ $library = Unicode::getStatus();
+
+ $requirements['unicode'] = [
+ 'title' => t('Unicode library'),
+ 'value' => $libraries[$library],
+ 'severity' => $severities[$library],
+ ];
+ switch ($failed_check) {
+ case 'mb_strlen':
+ $requirements['unicode']['description'] = t('Operations on Unicode strings are emulated on a best-effort basis. Install the <a href="http://php.net/mbstring">PHP mbstring extension</a> for improved Unicode support.');
+ break;
+
+ case 'mbstring.encoding_translation':
+ $requirements['unicode']['description'] = t('Multibyte string input conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.encoding_translation</em> setting. Refer to the <a href="http://php.net/mbstring">PHP mbstring documentation</a> for more information.');
+ break;
+ }
+
+ if ($phase == 'runtime') {
+ // Check for update status module.
+ if (!\Drupal::moduleHandler()->moduleExists('update')) {
+ $requirements['update status'] = [
+ 'value' => t('Not enabled'),
+ 'severity' => RequirementSeverity::Warning,
+ 'description' => t('Update notifications are not enabled. It is <strong>highly recommended</strong> that you install the Update Status module from the <a href=":module">module administration page</a> in order to stay up-to-date on new releases. For more information, <a href=":update">Update status handbook page</a>.', [
+ ':update' => 'https://www.drupal.org/documentation/modules/update',
+ ':module' => Url::fromRoute('system.modules_list')->toString(),
+ ]),
+ ];
+ }
+ else {
+ $requirements['update status'] = [
+ 'value' => t('Enabled'),
+ ];
+ }
+ $requirements['update status']['title'] = t('Update notifications');
+
+ if (Settings::get('rebuild_access')) {
+ $requirements['rebuild access'] = [
+ 'title' => t('Rebuild access'),
+ 'value' => t('Enabled'),
+ 'severity' => RequirementSeverity::Error,
+ 'description' => t('The rebuild_access setting is enabled in settings.php. It is recommended to have this setting disabled unless you are performing a rebuild.'),
+ ];
+ }
+ }
+
+ // Check if the SameSite cookie attribute is set to a valid value. Since
+ // this involves checking whether we are using a secure connection this
+ // only makes sense inside an HTTP request, not on the command line.
+ if ($phase === 'runtime' && PHP_SAPI !== 'cli') {
+ $samesite = ini_get('session.cookie_samesite') ?: t('Not set');
+ // Check if the SameSite attribute is set to a valid value. If it is set
+ // to 'None' the request needs to be done over HTTPS.
+ $valid = match ($samesite) {
+ 'Lax', 'Strict' => TRUE,
+ 'None' => $request_object->isSecure(),
+ default => FALSE,
+ };
+ $requirements['php_session_samesite'] = [
+ 'title' => t('SameSite cookie attribute'),
+ 'value' => $samesite,
+ 'severity' => $valid ? RequirementSeverity::OK : RequirementSeverity::Warning,
+ 'description' => t('This attribute should be explicitly set to Lax, Strict or None. If set to None then the request must be made via HTTPS. See <a href=":url" target="_blank">PHP documentation</a>', [
+ ':url' => 'https://www.php.net/manual/en/session.configuration.php#ini.session.cookie-samesite',
+ ]),
+ ];
+ }
+
+ // See if trusted host names have been configured, and warn the user if they
+ // are not set.
+ if ($phase == 'runtime') {
+ $trusted_host_patterns = Settings::get('trusted_host_patterns');
+ if (empty($trusted_host_patterns)) {
+ $requirements['trusted_host_patterns'] = [
+ 'title' => t('Trusted Host Settings'),
+ 'value' => t('Not enabled'),
+ 'description' => t('The trusted_host_patterns setting is not configured in settings.php. This can lead to security vulnerabilities. It is <strong>highly recommended</strong> that you configure this. See <a href=":url">Protecting against HTTP HOST Header attacks</a> for more information.', [':url' => 'https://www.drupal.org/docs/installing-drupal/trusted-host-settings']),
+ 'severity' => RequirementSeverity::Error,
+ ];
+ }
+ else {
+ $requirements['trusted_host_patterns'] = [
+ 'title' => t('Trusted Host Settings'),
+ 'value' => t('Enabled'),
+ 'description' => t('The trusted_host_patterns setting is set to allow %trusted_host_patterns', ['%trusted_host_patterns' => implode(', ', $trusted_host_patterns)]),
+ ];
+ }
+ }
+
+ // When the database driver is provided by a module, then check that the
+ // providing module is installed.
+ if ($phase === 'runtime' || $phase === 'update') {
+ $connection = Database::getConnection();
+ $provider = $connection->getProvider();
+ if ($provider !== 'core' && !\Drupal::moduleHandler()->moduleExists($provider)) {
+ $autoload = $connection->getConnectionOptions()['autoload'] ?? '';
+ if (str_contains($autoload, 'src/Driver/Database/')) {
+ $post_update_registry = \Drupal::service('update.post_update_registry');
+ $pending_updates = $post_update_registry->getPendingUpdateInformation();
+ if (!in_array('enable_provider_database_driver', array_keys($pending_updates['system']['pending'] ?? []), TRUE)) {
+ // Only show the warning when the post update function has run and
+ // the module that is providing the database driver is not
+ // installed.
+ $requirements['database_driver_provided_by_module'] = [
+ 'title' => t('Database driver provided by module'),
+ 'value' => t('Not installed'),
+ 'description' => t('The current database driver is provided by the module: %module. The module is currently not installed. You should immediately <a href=":install">install</a> the module.', ['%module' => $provider, ':install' => Url::fromRoute('system.modules_list')->toString()]),
+ 'severity' => RequirementSeverity::Error,
+ ];
+ }
+ }
+ }
+ }
+
+ // Check xdebug.max_nesting_level, as some pages will not work if it is too
+ // low.
+ if (extension_loaded('xdebug')) {
+ // Setting this value to 256 was considered adequate on Xdebug 2.3
+ // (see http://bugs.xdebug.org/bug_view_page.php?bug_id=00001100)
+ $minimum_nesting_level = 256;
+ $current_nesting_level = ini_get('xdebug.max_nesting_level');
+
+ if ($current_nesting_level < $minimum_nesting_level) {
+ $requirements['xdebug_max_nesting_level'] = [
+ 'title' => t('Xdebug settings'),
+ 'value' => t('xdebug.max_nesting_level is set to %value.', ['%value' => $current_nesting_level]),
+ 'description' => t('Set <code>xdebug.max_nesting_level=@level</code> in your PHP configuration as some pages in your Drupal site will not work when this setting is too low.', ['@level' => $minimum_nesting_level]),
+ 'severity' => RequirementSeverity::Error,
+ ];
+ }
+ }
+
+ // Installations on Windows can run into limitations with MAX_PATH if the
+ // Drupal root directory is too deep in the filesystem. Generally this
+ // shows up in cached Twig templates and other public files with long
+ // directory or file names. There is no definite root directory depth below
+ // which Drupal is guaranteed to function correctly on Windows. Since
+ // problems are likely with more than 100 characters in the Drupal root
+ // path, show an error.
+ if (str_starts_with(PHP_OS, 'WIN')) {
+ $depth = strlen(realpath(DRUPAL_ROOT . '/' . PublicStream::basePath()));
+ if ($depth > 120) {
+ $requirements['max_path_on_windows'] = [
+ 'title' => t('Windows installation depth'),
+ 'description' => t('The public files directory path is %depth characters. Paths longer than 120 characters will cause problems on Windows.', ['%depth' => $depth]),
+ 'severity' => RequirementSeverity::Error,
+ ];
+ }
+ }
+ // Check to see if dates will be limited to 1901-2038.
+ if (PHP_INT_SIZE <= 4) {
+ $requirements['limited_date_range'] = [
+ 'title' => t('Limited date range'),
+ 'value' => t('Your PHP installation has a limited date range.'),
+ 'description' => t('You are running on a system where PHP is compiled or limited to using 32-bit integers. This will limit the range of dates and timestamps to the years 1901-2038. Read about the <a href=":url">limitations of 32-bit PHP</a>.', [':url' => 'https://www.drupal.org/docs/system-requirements/limitations-of-32-bit-php']),
+ 'severity' => RequirementSeverity::Warning,
+ ];
+ }
+
+ // During installs from configuration don't support install profiles that
+ // implement hook_install.
+ if ($phase == 'install' && !empty($install_state['config_install_path'])) {
+ $install_hook = $install_state['parameters']['profile'] . '_install';
+ if (function_exists($install_hook)) {
+ $requirements['config_install'] = [
+ 'title' => t('Configuration install'),
+ 'value' => $install_state['parameters']['profile'],
+ 'description' => t('The selected profile has a hook_install() implementation and therefore can not be installed from configuration.'),
+ 'severity' => RequirementSeverity::Error,
+ ];
+ }
+ }
+
+ if ($phase === 'runtime') {
+ $settings = Settings::getAll();
+ if (array_key_exists('install_profile', $settings)) {
+ // The following message is only informational because not all site
+ // owners have access to edit their settings.php as it may be
+ // controlled by their hosting provider.
+ $requirements['install_profile_in_settings'] = [
+ 'title' => t('Install profile in settings'),
+ 'value' => t("Drupal 9 no longer uses the \$settings['install_profile'] value in settings.php and it should be removed."),
+ 'severity' => RequirementSeverity::Warning,
+ ];
+ }
+ }
+
+ // Ensure that no module has a current schema version that is lower than the
+ // one that was last removed.
+ if ($phase == 'update') {
+ $module_handler = \Drupal::moduleHandler();
+ /** @var \Drupal\Core\Update\UpdateHookRegistry $update_registry */
+ $update_registry = \Drupal::service('update.update_hook_registry');
+ $module_list = [];
+ // hook_update_last_removed() is a procedural hook hook because we
+ // do not have classes loaded that would be needed.
+ // Simply inlining the old hook mechanism is better than making
+ // ModuleInstaller::invoke() public.
+ foreach ($module_handler->getModuleList() as $module => $extension) {
+ $function = $module . '_update_last_removed';
+ if (function_exists($function)) {
+ $last_removed = $function();
+ if ($last_removed && $last_removed > $update_registry->getInstalledVersion($module)) {
+
+ /** @var \Drupal\Core\Extension\Extension $module_info */
+ $module_info = $module_extension_list->get($module);
+ $module_list[$module] = [
+ 'name' => $module_info->info['name'],
+ 'last_removed' => $last_removed,
+ 'installed_version' => $update_registry->getInstalledVersion($module),
+ ];
+ }
+ }
+ }
+
+ // If user module is in the list then only show a specific message for
+ // Drupal core.
+ if (isset($module_list['user'])) {
+ $requirements['user_update_last_removed'] = [
+ 'title' => t('The version of Drupal you are trying to update from is too old'),
+ 'description' => t('Updating to Drupal @current_major is only supported from Drupal version @required_min_version or higher. If you are trying to update from an older version, first update to the latest version of Drupal @previous_major. (<a href=":url">Drupal upgrade guide</a>)', [
+ '@current_major' => 10,
+ '@required_min_version' => '9.4.0',
+ '@previous_major' => 9,
+ ':url' => 'https://www.drupal.org/docs/upgrading-drupal/drupal-8-and-higher',
+ ]),
+ 'severity' => RequirementSeverity::Error,
+ ];
+ }
+ else {
+ foreach ($module_list as $module => $data) {
+ $requirements[$module . '_update_last_removed'] = [
+ 'title' => t('Unsupported schema version: @module', ['@module' => $data['name']]),
+ 'description' => t('The installed version of the %module module is too old to update. Update to an intermediate version first (last removed version: @last_removed_version, installed version: @installed_version).', [
+ '%module' => $data['name'],
+ '@last_removed_version' => $data['last_removed'],
+ '@installed_version' => $data['installed_version'],
+ ]),
+ 'severity' => RequirementSeverity::Error,
+ ];
+ }
+ }
+ // Also check post-updates. Only do this if we're not already showing an
+ // error for hook_update_N().
+ $missing_updates = [];
+ if (empty($module_list)) {
+ $existing_updates = \Drupal::service('keyvalue')->get('post_update')->get('existing_updates', []);
+ $post_update_registry = \Drupal::service('update.post_update_registry');
+ $modules = \Drupal::moduleHandler()->getModuleList();
+ foreach ($modules as $module => $extension) {
+ $module_info = $module_extension_list->get($module);
+ $removed_post_updates = $post_update_registry->getRemovedPostUpdates($module);
+ if ($missing_updates = array_diff(array_keys($removed_post_updates), $existing_updates)) {
+ $versions = array_unique(array_intersect_key($removed_post_updates, array_flip($missing_updates)));
+ $description = new PluralTranslatableMarkup(count($versions),
+ 'The installed version of the %module module is too old to update. Update to a version prior to @versions first (missing updates: @missing_updates).',
+ 'The installed version of the %module module is too old to update. Update first to a version prior to all of the following: @versions (missing updates: @missing_updates).',
+ [
+ '%module' => $module_info->info['name'],
+ '@missing_updates' => implode(', ', $missing_updates),
+ '@versions' => implode(', ', $versions),
+ ]
+ );
+ $requirements[$module . '_post_update_removed'] = [
+ 'title' => t('Missing updates for: @module', ['@module' => $module_info->info['name']]),
+ 'description' => $description,
+ 'severity' => RequirementSeverity::Error,
+ ];
+ }
+ }
+ }
+
+ if (empty($missing_updates)) {
+ foreach ($update_registry->getAllEquivalentUpdates() as $module => $equivalent_updates) {
+ $module_info = $module_extension_list->get($module);
+ foreach ($equivalent_updates as $future_update => $data) {
+ $future_update_function_name = $module . '_update_' . $future_update;
+ $ran_update_function_name = $module . '_update_' . $data['ran_update'];
+ // If an update was marked as an equivalent by a previous update,
+ // and both the previous update and the equivalent update are not
+ // found in the current code base, prevent updating. This indicates
+ // a site attempting to go 'backwards' in terms of database schema.
+ // @see \Drupal\Core\Update\UpdateHookRegistry::markFutureUpdateEquivalent()
+ if (!function_exists($ran_update_function_name) && !function_exists($future_update_function_name)) {
+ // If the module is provided by core prepend helpful text as the
+ // module does not exist in composer or Drupal.org.
+ if (str_starts_with($module_info->getPathname(), 'core/')) {
+ $future_version_string = 'Drupal Core ' . $data['future_version_string'];
+ }
+ else {
+ $future_version_string = $data['future_version_string'];
+ }
+ $requirements[$module . '_equivalent_update_missing'] = [
+ 'title' => t('Missing updates for: @module', ['@module' => $module_info->info['name']]),
+ 'description' => t('The version of the %module module that you are attempting to update to is missing update @future_update (which was marked as an equivalent by @ran_update). Update to at least @future_version_string.', [
+ '%module' => $module_info->info['name'],
+ '@ran_update' => $data['ran_update'],
+ '@future_update' => $future_update,
+ '@future_version_string' => $future_version_string,
+ ]),
+ 'severity' => RequirementSeverity::Error,
+ ];
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Add warning when twig debug option is enabled.
+ if ($phase === 'runtime') {
+ $development_settings = \Drupal::keyValue('development_settings');
+ $twig_debug = $development_settings->get('twig_debug', FALSE);
+ $twig_cache_disable = $development_settings->get('twig_cache_disable', FALSE);
+ if ($twig_debug || $twig_cache_disable) {
+ $requirements['twig_debug_enabled'] = [
+ 'title' => t('Twig development mode'),
+ 'value' => t('Twig development mode settings are turned on. Go to @link to disable them.', [
+ '@link' => Link::createFromRoute(
+ 'development settings page',
+ 'system.development_settings',
+ )->toString(),
+ ]),
+ 'severity' => RequirementSeverity::Warning,
+ ];
+ }
+ $render_cache_disabled = $development_settings->get('disable_rendered_output_cache_bins', FALSE);
+ if ($render_cache_disabled) {
+ $requirements['render_cache_disabled'] = [
+ 'title' => t('Markup caching disabled'),
+ 'value' => t('Render cache, dynamic page cache, and page cache are bypassed. Go to @link to enable them.', [
+ '@link' => Link::createFromRoute(
+ 'development settings page',
+ 'system.development_settings',
+ )->toString(),
+ ]),
+ 'severity' => RequirementSeverity::Warning,
+ ];
+ }
+ }
+
+ return $requirements;
+ }
+
+ /**
+ * Display requirements from security advisories.
+ *
+ * @param array[] $requirements
+ * The requirements array as specified in hook_requirements().
+ */
+ public static function systemAdvisoriesRequirements(array &$requirements): void {
+ if (!\Drupal::config('system.advisories')->get('enabled')) {
+ return;
+ }
+
+ /** @var \Drupal\system\SecurityAdvisories\SecurityAdvisoriesFetcher $fetcher */
+ $fetcher = \Drupal::service('system.sa_fetcher');
+ try {
+ $advisories = $fetcher->getSecurityAdvisories(TRUE, 5);
+ }
+ catch (ClientExceptionInterface $exception) {
+ $requirements['system_advisories']['title'] = t('Critical security announcements');
+ $requirements['system_advisories']['severity'] = RequirementSeverity::Warning;
+ $requirements['system_advisories']['description'] = ['#theme' => 'system_security_advisories_fetch_error_message'];
+ Error::logException(\Drupal::logger('system'), $exception, 'Failed to retrieve security advisory data.');
+ return;
+ }
+
+ if (!empty($advisories)) {
+ $advisory_links = [];
+ $severity = RequirementSeverity::Warning;
+ foreach ($advisories as $advisory) {
+ if (!$advisory->isPsa()) {
+ $severity = RequirementSeverity::Error;
+ }
+ $advisory_links[] = new Link($advisory->getTitle(), Url::fromUri($advisory->getUrl()));
+ }
+ $requirements['system_advisories']['title'] = t('Critical security announcements');
+ $requirements['system_advisories']['severity'] = $severity;
+ $requirements['system_advisories']['description'] = [
+ 'list' => [
+ '#theme' => 'item_list',
+ '#items' => $advisory_links,
+ ],
+ ];
+ if (\Drupal::moduleHandler()->moduleExists('help')) {
+ $requirements['system_advisories']['description']['help_link'] = Link::createFromRoute(
+ 'What are critical security announcements?',
+ 'help.page', ['name' => 'system'],
+ ['fragment' => 'security-advisories']
+ )->toRenderable();
+ }
+ }
+ }
+
+}
diff --git a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php
index 670fbc06cf7..2ce434e0fd3 100644
--- a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php
+++ b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php
@@ -4,6 +4,7 @@ namespace Drupal\system\Plugin\ImageToolkit;
use Drupal\Component\Utility\Color;
use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\File\Exception\FileException;
use Drupal\Core\File\FileExists;
use Drupal\Core\File\FileSystemInterface;
@@ -439,9 +440,6 @@ class GDToolkit extends ImageToolkitBase {
IMG_AVIF => 'AVIF',
];
$supported_formats = array_filter($check_formats, fn($type) => imagetypes() & $type, ARRAY_FILTER_USE_KEY);
- if (isset($supported_formats[IMG_AVIF]) && !$this->checkAvifSupport()) {
- unset($supported_formats[IMG_AVIF]);
- }
$unsupported_formats = array_diff_key($check_formats, $supported_formats);
$descriptions = [];
@@ -454,7 +452,7 @@ class GDToolkit extends ImageToolkitBase {
);
}
if ($unsupported_formats) {
- $requirements['version']['severity'] = REQUIREMENT_WARNING;
+ $requirements['version']['severity'] = RequirementSeverity::Warning;
$unsupported = $this->formatPlural(
count($unsupported_formats),
'Unsupported image file format: %formats.',
@@ -475,7 +473,7 @@ class GDToolkit extends ImageToolkitBase {
// Check for filter and rotate support.
if (!function_exists('imagefilter') || !function_exists('imagerotate')) {
- $requirements['version']['severity'] = REQUIREMENT_WARNING;
+ $requirements['version']['severity'] = RequirementSeverity::Warning;
$descriptions[] = $this->t('The GD Library for PHP is enabled, but was compiled without support for functions used by the rotate and desaturate effects. It was probably compiled using the official GD libraries from the <a href="https://libgd.github.io/">gdLibrary site</a> instead of the GD library bundled with PHP. You should recompile PHP --with-gd using the bundled GD library. See <a href="https://www.php.net/manual/book.image.php">the PHP manual</a>.');
}
@@ -556,7 +554,7 @@ class GDToolkit extends ImageToolkitBase {
* @return bool
* TRUE if AVIF is fully supported, FALSE otherwise.
*/
- protected function checkAvifSupport(): bool {
+ protected static function checkAvifSupport(): bool {
static $supported = NULL;
if ($supported !== NULL) {
@@ -564,7 +562,7 @@ class GDToolkit extends ImageToolkitBase {
}
$tempFile = fopen('php://memory', 'r+');
- $supported = imageavif(imagecreatetruecolor(1, 1), $tempFile, 0, 10) && fstat($tempFile)['size'] > 0;
+ $supported = function_exists('imageavif') && imageavif(imagecreatetruecolor(1, 1), $tempFile, 0, 10) && fstat($tempFile)['size'] > 0;
fclose($tempFile);
return $supported;
@@ -578,13 +576,16 @@ class GDToolkit extends ImageToolkitBase {
* IMAGETYPE_* constant (e.g. IMAGETYPE_JPEG, IMAGETYPE_PNG, etc.).
*/
protected static function supportedTypes() {
- return [
+ $types = [
IMAGETYPE_PNG,
IMAGETYPE_JPEG,
IMAGETYPE_GIF,
IMAGETYPE_WEBP,
- IMAGETYPE_AVIF,
];
+ if (static::checkAvifSupport()) {
+ $types[] = IMAGETYPE_AVIF;
+ }
+ return $types;
}
}
diff --git a/core/modules/system/src/SystemManager.php b/core/modules/system/src/SystemManager.php
index 5534e70147b..563188e017f 100644
--- a/core/modules/system/src/SystemManager.php
+++ b/core/modules/system/src/SystemManager.php
@@ -2,11 +2,12 @@
namespace Drupal\system;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\Menu\MenuActiveTrailInterface;
-use Drupal\Core\Menu\MenuLinkTreeInterface;
use Drupal\Core\Menu\MenuLinkInterface;
+use Drupal\Core\Menu\MenuLinkTreeInterface;
use Drupal\Core\Menu\MenuTreeParameters;
-use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\HttpFoundation\RequestStack;
@@ -54,16 +55,31 @@ class SystemManager {
/**
* Requirement severity -- Requirement successfully met.
+ *
+ * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
+ * \Drupal\Core\Extension\Requirement\RequirementSeverity::OK instead.
+ *
+ * @see https://www.drupal.org/node/3410939
*/
const REQUIREMENT_OK = 0;
/**
* Requirement severity -- Warning condition; proceed but flag warning.
+ *
+ * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
+ * \Drupal\Core\Extension\Requirement\RequirementSeverity::Warning instead.
+ *
+ * @see https://www.drupal.org/node/3410939
*/
const REQUIREMENT_WARNING = 1;
/**
* Requirement severity -- Error condition; abort installation.
+ *
+ * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
+ * \Drupal\Core\Extension\Requirement\RequirementSeverity::Error instead.
+ *
+ * @see https://www.drupal.org/node/3410939
*/
const REQUIREMENT_ERROR = 2;
@@ -94,7 +110,7 @@ class SystemManager {
*/
public function checkRequirements() {
$requirements = $this->listRequirements();
- return $this->getMaxSeverity($requirements) == static::REQUIREMENT_ERROR;
+ return RequirementSeverity::maxSeverityFromRequirements($requirements) === RequirementSeverity::Error;
}
/**
@@ -104,7 +120,7 @@ class SystemManager {
* An array of system requirements.
*/
public function listRequirements() {
- // Load .install files
+ // Load .install files.
include_once DRUPAL_ROOT . '/core/includes/install.inc';
drupal_load_updates();
@@ -136,15 +152,16 @@ class SystemManager {
*
* @return int
* The highest severity in the array.
+ *
+ * @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use
+ * \Drupal\Core\Extension\Requirement\RequirementSeverity::getMaxSeverity()
+ * instead.
+ *
+ * @see https://www.drupal.org/node/3410939
*/
public function getMaxSeverity(&$requirements) {
- $severity = static::REQUIREMENT_OK;
- foreach ($requirements as $requirement) {
- if (isset($requirement['severity'])) {
- $severity = max($severity, $requirement['severity']);
- }
- }
- return $severity;
+ @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use ' . RequirementSeverity::class . '::maxSeverityFromRequirements() instead. See https://www.drupal.org/node/3410939', \E_USER_DEPRECATED);
+ return RequirementSeverity::maxSeverityFromRequirements($requirements)->value;
}
/**
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 9b8c25c157e..5bbaa8a5436 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -5,30 +5,9 @@
* Install, update and uninstall functions for the system module.
*/
-use Drupal\Component\FileSystem\FileSystem as FileSystemComponent;
-use Drupal\Component\Utility\Bytes;
use Drupal\Component\Utility\Crypt;
-use Drupal\Component\Utility\Environment;
-use Drupal\Component\Utility\OpCodeCache;
-use Drupal\Component\Utility\Unicode;
-use Drupal\Core\Database\Database;
-use Drupal\Core\DrupalKernel;
-use Drupal\Core\Extension\ExtensionLifecycle;
-use Drupal\Core\File\FileSystemInterface;
-use Drupal\Core\Link;
-use Drupal\Core\Utility\PhpRequirements;
-use Drupal\Core\Render\Markup;
-use Drupal\Core\Site\Settings;
-use Drupal\Core\StreamWrapper\PrivateStream;
-use Drupal\Core\StreamWrapper\PublicStream;
-use Drupal\Core\StringTranslation\ByteSizeMarkup;
-use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Update\EquivalentUpdate;
-use Drupal\Core\Url;
-use Drupal\Core\Utility\Error;
-use Psr\Http\Client\ClientExceptionInterface;
-use Symfony\Component\HttpFoundation\Request;
// cspell:ignore quickedit
@@ -61,1565 +40,6 @@ const DRUPAL_CORE_REMOVED_THEME_LIST = [
];
/**
- * Implements hook_requirements().
- */
-function system_requirements($phase): array {
- global $install_state;
-
- // Get the current default PHP requirements for this version of Drupal.
- $minimum_supported_php = PhpRequirements::getMinimumSupportedPhp();
-
- // Reset the extension lists.
- /** @var \Drupal\Core\Extension\ModuleExtensionList $module_extension_list */
- $module_extension_list = \Drupal::service('extension.list.module');
- $module_extension_list->reset();
- /** @var \Drupal\Core\Extension\ThemeExtensionList $theme_extension_list */
- $theme_extension_list = \Drupal::service('extension.list.theme');
- $theme_extension_list->reset();
- $requirements = [];
-
- // Report Drupal version
- if ($phase == 'runtime') {
- $requirements['drupal'] = [
- 'title' => t('Drupal'),
- 'value' => \Drupal::VERSION,
- 'severity' => REQUIREMENT_INFO,
- 'weight' => -10,
- ];
-
- // Display the currently active installation profile, if the site
- // is not running the default installation profile.
- $profile = \Drupal::installProfile();
- if ($profile != 'standard' && !empty($profile)) {
- $info = $module_extension_list->getExtensionInfo($profile);
- $requirements['install_profile'] = [
- 'title' => t('Installation profile'),
- 'value' => t('%profile_name (%profile%version)', [
- '%profile_name' => $info['name'],
- '%profile' => $profile,
- '%version' => !empty($info['version']) ? '-' . $info['version'] : '',
- ]),
- 'severity' => REQUIREMENT_INFO,
- 'weight' => -9,
- ];
- }
-
- // Gather all obsolete and experimental modules being enabled.
- $obsolete_extensions = [];
- $deprecated_modules = [];
- $experimental_modules = [];
- $enabled_modules = \Drupal::moduleHandler()->getModuleList();
- foreach ($enabled_modules as $module => $data) {
- $info = $module_extension_list->getExtensionInfo($module);
- if (isset($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER])) {
- if ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::EXPERIMENTAL) {
- $experimental_modules[$module] = $info['name'];
- }
- elseif ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::DEPRECATED) {
- $deprecated_modules[] = ['name' => $info['name'], 'lifecycle_link' => $info['lifecycle_link']];
- }
- elseif ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::OBSOLETE) {
- $obsolete_extensions[$module] = ['name' => $info['name'], 'lifecycle_link' => $info['lifecycle_link']];
- }
- }
- }
-
- // Warn if any experimental modules are installed.
- if (!empty($experimental_modules)) {
- $requirements['experimental_modules'] = [
- 'title' => t('Experimental modules installed'),
- 'value' => t('Experimental modules found: %module_list. <a href=":url">Experimental modules</a> are provided for testing purposes only. Use at your own risk.', ['%module_list' => implode(', ', $experimental_modules), ':url' => 'https://www.drupal.org/core/experimental']),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
- // Warn if any deprecated modules are installed.
- if (!empty($deprecated_modules)) {
- foreach ($deprecated_modules as $deprecated_module) {
- $deprecated_modules_link_list[] = (string) Link::fromTextAndUrl($deprecated_module['name'], Url::fromUri($deprecated_module['lifecycle_link']))->toString();
- }
- $requirements['deprecated_modules'] = [
- 'title' => t('Deprecated modules installed'),
- 'value' => t('Deprecated modules found: %module_list.', [
- '%module_list' => Markup::create(implode(', ', $deprecated_modules_link_list)),
- ]),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
-
- // Gather all obsolete and experimental themes being installed.
- $experimental_themes = [];
- $deprecated_themes = [];
- $installed_themes = \Drupal::service('theme_handler')->listInfo();
- foreach ($installed_themes as $theme => $data) {
- $info = $theme_extension_list->getExtensionInfo($theme);
- if (isset($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER])) {
- if ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::EXPERIMENTAL) {
- $experimental_themes[$theme] = $info['name'];
- }
- elseif ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::DEPRECATED) {
- $deprecated_themes[] = ['name' => $info['name'], 'lifecycle_link' => $info['lifecycle_link']];
- }
- elseif ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::OBSOLETE) {
- $obsolete_extensions[$theme] = ['name' => $info['name'], 'lifecycle_link' => $info['lifecycle_link']];
- }
- }
- }
-
- // Warn if any experimental themes are installed.
- if (!empty($experimental_themes)) {
- $requirements['experimental_themes'] = [
- 'title' => t('Experimental themes installed'),
- 'value' => t('Experimental themes found: %theme_list. Experimental themes are provided for testing purposes only. Use at your own risk.', ['%theme_list' => implode(', ', $experimental_themes)]),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
-
- // Warn if any deprecated themes are installed.
- if (!empty($deprecated_themes)) {
- foreach ($deprecated_themes as $deprecated_theme) {
- $deprecated_themes_link_list[] = (string) Link::fromTextAndUrl($deprecated_theme['name'], Url::fromUri($deprecated_theme['lifecycle_link']))->toString();
-
- }
- $requirements['deprecated_themes'] = [
- 'title' => t('Deprecated themes installed'),
- 'value' => t('Deprecated themes found: %theme_list.', [
- '%theme_list' => Markup::create(implode(', ', $deprecated_themes_link_list)),
- ]),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
-
- // Warn if any obsolete extensions (themes or modules) are installed.
- if (!empty($obsolete_extensions)) {
- foreach ($obsolete_extensions as $obsolete_extension) {
- $obsolete_extensions_link_list[] = (string) Link::fromTextAndUrl($obsolete_extension['name'], Url::fromUri($obsolete_extension['lifecycle_link']))->toString();
- }
- $requirements['obsolete_extensions'] = [
- 'title' => t('Obsolete extensions installed'),
- 'value' => t('Obsolete extensions found: %extensions. Obsolete extensions are provided only so that they can be uninstalled cleanly. You should immediately <a href=":uninstall_url">uninstall these extensions</a> since they may be removed in a future release.', [
- '%extensions' => Markup::create(implode(', ', $obsolete_extensions_link_list)),
- ':uninstall_url' => Url::fromRoute('system.modules_uninstall')->toString(),
- ]),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
- _system_advisories_requirements($requirements);
- }
-
- // Web server information.
- $request_object = \Drupal::request();
- $software = $request_object->server->get('SERVER_SOFTWARE');
- $requirements['webserver'] = [
- 'title' => t('Web server'),
- 'value' => $software,
- ];
-
- // Tests clean URL support.
- if ($phase == 'install' && $install_state['interactive'] && !$request_object->query->has('rewrite') && str_contains($software, 'Apache')) {
- // If the Apache rewrite module is not enabled, Apache version must be >=
- // 2.2.16 because of the FallbackResource directive in the root .htaccess
- // file. Since the Apache version reported by the server is dependent on the
- // ServerTokens setting in httpd.conf, we may not be able to determine if a
- // given config is valid. Thus we are unable to use version_compare() as we
- // need have three possible outcomes: the version of Apache is greater than
- // 2.2.16, is less than 2.2.16, or cannot be determined accurately. In the
- // first case, we encourage the use of mod_rewrite; in the second case, we
- // raise an error regarding the minimum Apache version; in the third case,
- // we raise a warning that the current version of Apache may not be
- // supported.
- $rewrite_warning = FALSE;
- $rewrite_error = FALSE;
- $apache_version_string = 'Apache';
-
- // Determine the Apache version number: major, minor and revision.
- if (preg_match('/Apache\/(\d+)\.?(\d+)?\.?(\d+)?/', $software, $matches)) {
- $apache_version_string = $matches[0];
-
- // Major version number
- if ($matches[1] < 2) {
- $rewrite_error = TRUE;
- }
- elseif ($matches[1] == 2) {
- if (!isset($matches[2])) {
- $rewrite_warning = TRUE;
- }
- elseif ($matches[2] < 2) {
- $rewrite_error = TRUE;
- }
- elseif ($matches[2] == 2) {
- if (!isset($matches[3])) {
- $rewrite_warning = TRUE;
- }
- elseif ($matches[3] < 16) {
- $rewrite_error = TRUE;
- }
- }
- }
- }
- else {
- $rewrite_warning = TRUE;
- }
-
- if ($rewrite_warning) {
- $requirements['apache_version'] = [
- 'title' => t('Apache version'),
- 'value' => $apache_version_string,
- 'severity' => REQUIREMENT_WARNING,
- 'description' => t('Due to the settings for ServerTokens in httpd.conf, it is impossible to accurately determine the version of Apache running on this server. The reported value is @reported, to run Drupal without mod_rewrite, a minimum version of 2.2.16 is needed.', ['@reported' => $apache_version_string]),
- ];
- }
-
- if ($rewrite_error) {
- $requirements['Apache version'] = [
- 'title' => t('Apache version'),
- 'value' => $apache_version_string,
- 'severity' => REQUIREMENT_ERROR,
- 'description' => t('The minimum version of Apache needed to run Drupal without mod_rewrite enabled is 2.2.16. See the <a href=":link">enabling clean URLs</a> page for more information on mod_rewrite.', [':link' => 'https://www.drupal.org/docs/8/clean-urls-in-drupal-8']),
- ];
- }
-
- if (!$rewrite_error && !$rewrite_warning) {
- $requirements['rewrite_module'] = [
- 'title' => t('Clean URLs'),
- 'value' => t('Disabled'),
- 'severity' => REQUIREMENT_WARNING,
- 'description' => t('Your server is capable of using clean URLs, but it is not enabled. Using clean URLs gives an improved user experience and is recommended. <a href=":link">Enable clean URLs</a>', [':link' => 'https://www.drupal.org/docs/8/clean-urls-in-drupal-8']),
- ];
- }
- }
-
- // Verify the user is running a supported PHP version.
- // If the site is running a recommended version of PHP, just display it
- // as an informational message on the status report. This will be overridden
- // with an error or warning if the site is running older PHP versions for
- // which Drupal has already or will soon drop support.
- $phpversion = $phpversion_label = phpversion();
- if ($phase === 'runtime') {
- $phpversion_label = t('@phpversion (<a href=":url">more information</a>)', [
- '@phpversion' => $phpversion,
- ':url' => (new Url('system.php'))->toString(),
- ]);
- }
- $requirements['php'] = [
- 'title' => t('PHP'),
- 'value' => $phpversion_label,
- ];
-
- // Check if the PHP version is below what Drupal supports.
- if (version_compare($phpversion, $minimum_supported_php) < 0) {
- $requirements['php']['description'] = t('Your PHP installation is too old. Drupal requires at least PHP %version. It is recommended to upgrade to PHP version %recommended or higher for the best ongoing support. See <a href="http://php.net/supported-versions.php">PHP\'s version support documentation</a> and the <a href=":php_requirements">Drupal PHP requirements</a> page for more information.',
- [
- '%version' => $minimum_supported_php,
- '%recommended' => \Drupal::RECOMMENDED_PHP,
- ':php_requirements' => 'https://www.drupal.org/docs/system-requirements/php-requirements',
- ]
- );
-
- // If the PHP version is also below the absolute minimum allowed, it's not
- // safe to continue with the requirements check, and should always be an
- // error.
- if (version_compare($phpversion, \Drupal::MINIMUM_PHP) < 0) {
- $requirements['php']['severity'] = REQUIREMENT_ERROR;
- return $requirements;
- }
- // Otherwise, the message should be an error at runtime, and a warning
- // during installation or update.
- $requirements['php']['severity'] = ($phase === 'runtime') ? REQUIREMENT_ERROR : REQUIREMENT_WARNING;
- }
- // For PHP versions that are still supported but no longer recommended,
- // inform users of what's recommended, allowing them to take action before it
- // becomes urgent.
- elseif ($phase === 'runtime' && version_compare($phpversion, \Drupal::RECOMMENDED_PHP) < 0) {
- $requirements['php']['description'] = t('It is recommended to upgrade to PHP version %recommended or higher for the best ongoing support. See <a href="http://php.net/supported-versions.php">PHP\'s version support documentation</a> and the <a href=":php_requirements">Drupal PHP requirements</a> page for more information.', ['%recommended' => \Drupal::RECOMMENDED_PHP, ':php_requirements' => 'https://www.drupal.org/docs/system-requirements/php-requirements']);
- $requirements['php']['severity'] = REQUIREMENT_INFO;
- }
-
- // Test for PHP extensions.
- $requirements['php_extensions'] = [
- 'title' => t('PHP extensions'),
- ];
-
- $missing_extensions = [];
- $required_extensions = [
- 'date',
- 'dom',
- 'filter',
- 'gd',
- 'hash',
- 'json',
- 'pcre',
- 'pdo',
- 'session',
- 'SimpleXML',
- 'SPL',
- 'tokenizer',
- 'xml',
- 'zlib',
- ];
- foreach ($required_extensions as $extension) {
- if (!extension_loaded($extension)) {
- $missing_extensions[] = $extension;
- }
- }
-
- if (!empty($missing_extensions)) {
- $description = t('Drupal requires you to enable the PHP extensions in the following list (see the <a href=":system_requirements">system requirements page</a> for more information):', [
- ':system_requirements' => 'https://www.drupal.org/docs/system-requirements',
- ]);
-
- // We use twig inline_template to avoid twig's autoescape.
- $description = [
- '#type' => 'inline_template',
- '#template' => '{{ description }}{{ missing_extensions }}',
- '#context' => [
- 'description' => $description,
- 'missing_extensions' => [
- '#theme' => 'item_list',
- '#items' => $missing_extensions,
- ],
- ],
- ];
-
- $requirements['php_extensions']['value'] = t('Disabled');
- $requirements['php_extensions']['severity'] = REQUIREMENT_ERROR;
- $requirements['php_extensions']['description'] = $description;
- }
- else {
- $requirements['php_extensions']['value'] = t('Enabled');
- }
-
- if ($phase == 'install' || $phase == 'runtime') {
- // Check to see if OPcache is installed.
- if (!OpCodeCache::isEnabled()) {
- $requirements['php_opcache'] = [
- 'value' => t('Not enabled'),
- 'severity' => REQUIREMENT_WARNING,
- 'description' => t('PHP OPcode caching can improve your site\'s performance considerably. It is <strong>highly recommended</strong> to have <a href="http://php.net/manual/opcache.installation.php" target="_blank">OPcache</a> installed on your server.'),
- ];
- }
- else {
- $requirements['php_opcache']['value'] = t('Enabled');
- }
- $requirements['php_opcache']['title'] = t('PHP OPcode caching');
- }
-
- // Check to see if APCu is installed and configured correctly.
- if ($phase == 'runtime' && PHP_SAPI != 'cli') {
- $requirements['php_apcu_enabled']['title'] = t('PHP APCu caching');
- $requirements['php_apcu_available']['title'] = t('PHP APCu available caching');
- if (extension_loaded('apcu') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN)) {
- $memory_info = apcu_sma_info(TRUE);
- $apcu_actual_size = ByteSizeMarkup::create($memory_info['seg_size'] * $memory_info['num_seg']);
- $apcu_recommended_size = '32 MB';
- $requirements['php_apcu_enabled']['value'] = t('Enabled (@size)', ['@size' => $apcu_actual_size]);
- if (Bytes::toNumber(ini_get('apc.shm_size')) * ini_get('apc.shm_segments') < Bytes::toNumber($apcu_recommended_size)) {
- $requirements['php_apcu_enabled']['severity'] = REQUIREMENT_WARNING;
- $requirements['php_apcu_enabled']['description'] = t('Depending on your configuration, Drupal can run with a @apcu_size APCu limit. However, a @apcu_default_size APCu limit (the default) or above is recommended, especially if your site uses additional custom or contributed modules.', [
- '@apcu_size' => $apcu_actual_size,
- '@apcu_default_size' => $apcu_recommended_size,
- ]);
- }
- else {
- $memory_available = $memory_info['avail_mem'] / ($memory_info['seg_size'] * $memory_info['num_seg']);
- if ($memory_available < 0.1) {
- $requirements['php_apcu_available']['severity'] = REQUIREMENT_ERROR;
- $requirements['php_apcu_available']['description'] = t('APCu is using over 90% of its allotted memory (@apcu_actual_size). To improve APCu performance, consider increasing this limit.', [
- '@apcu_actual_size' => $apcu_actual_size,
- ]);
- }
- elseif ($memory_available < 0.25) {
- $requirements['php_apcu_available']['severity'] = REQUIREMENT_WARNING;
- $requirements['php_apcu_available']['description'] = t('APCu is using over 75% of its allotted memory (@apcu_actual_size). To improve APCu performance, consider increasing this limit.', [
- '@apcu_actual_size' => $apcu_actual_size,
- ]);
- }
- else {
- $requirements['php_apcu_available']['severity'] = REQUIREMENT_OK;
- }
- $requirements['php_apcu_available']['value'] = t('Memory available: @available.', [
- '@available' => ByteSizeMarkup::create($memory_info['avail_mem']),
- ]);
- }
- }
- else {
- $requirements['php_apcu_enabled'] += [
- 'value' => t('Not enabled'),
- 'severity' => REQUIREMENT_INFO,
- 'description' => t('PHP APCu caching can improve your site\'s performance considerably. It is <strong>highly recommended</strong> to have <a href="https://www.php.net/manual/apcu.installation.php" target="_blank">APCu</a> installed on your server.'),
- ];
- }
- }
-
- if ($phase != 'update') {
- // Test whether we have a good source of random bytes.
- $requirements['php_random_bytes'] = [
- 'title' => t('Random number generation'),
- ];
- try {
- $bytes = random_bytes(10);
- if (strlen($bytes) != 10) {
- throw new \Exception("Tried to generate 10 random bytes, generated '" . strlen($bytes) . "'");
- }
- $requirements['php_random_bytes']['value'] = t('Successful');
- }
- catch (\Exception $e) {
- // If /dev/urandom is not available on a UNIX-like system, check whether
- // open_basedir restrictions are the cause.
- $open_basedir_blocks_urandom = FALSE;
- if (DIRECTORY_SEPARATOR === '/' && !@is_readable('/dev/urandom')) {
- $open_basedir = ini_get('open_basedir');
- if ($open_basedir) {
- $open_basedir_paths = explode(PATH_SEPARATOR, $open_basedir);
- $open_basedir_blocks_urandom = !array_intersect(['/dev', '/dev/', '/dev/urandom'], $open_basedir_paths);
- }
- }
- $args = [
- ':drupal-php' => 'https://www.drupal.org/docs/system-requirements/php-requirements',
- '%exception_message' => $e->getMessage(),
- ];
- if ($open_basedir_blocks_urandom) {
- $requirements['php_random_bytes']['description'] = t('Drupal is unable to generate highly randomized numbers, which means certain security features like password reset URLs are not as secure as they should be. Instead, only a slow, less-secure fallback generator is available. The most likely cause is that open_basedir restrictions are in effect and /dev/urandom is not on the allowed list. See the <a href=":drupal-php">system requirements</a> page for more information. %exception_message', $args);
- }
- else {
- $requirements['php_random_bytes']['description'] = t('Drupal is unable to generate highly randomized numbers, which means certain security features like password reset URLs are not as secure as they should be. Instead, only a slow, less-secure fallback generator is available. See the <a href=":drupal-php">system requirements</a> page for more information. %exception_message', $args);
- }
- $requirements['php_random_bytes']['value'] = t('Less secure');
- $requirements['php_random_bytes']['severity'] = REQUIREMENT_ERROR;
- }
- }
-
- if ($phase === 'runtime' && PHP_SAPI !== 'cli') {
- if (!function_exists('fastcgi_finish_request') && !function_exists('litespeed_finish_request') && !ob_get_status()) {
- $requirements['output_buffering'] = [
- 'title' => t('Output Buffering'),
- 'error_value' => t('Not enabled'),
- 'severity' => REQUIREMENT_WARNING,
- 'description' => t('<a href="https://www.php.net/manual/en/function.ob-start.php">Output buffering</a> is not enabled. This may degrade Drupal\'s performance. You can enable output buffering by default <a href="https://www.php.net/manual/en/outcontrol.configuration.php#ini.output-buffering">in your PHP settings</a>.'),
- ];
- }
- }
-
- if ($phase == 'install' || $phase == 'update') {
- // Test for PDO (database).
- $requirements['database_extensions'] = [
- 'title' => t('Database support'),
- ];
-
- // Make sure PDO is available.
- $database_ok = extension_loaded('pdo');
- if (!$database_ok) {
- $pdo_message = t('Your web server does not appear to support PDO (PHP Data Objects). Ask your hosting provider if they support the native PDO extension. See the <a href=":link">system requirements</a> page for more information.', [
- ':link' => 'https://www.drupal.org/docs/system-requirements/php-requirements#database',
- ]);
- }
- else {
- // Make sure at least one supported database driver exists.
- if (empty(Database::getDriverList()->getInstallableList())) {
- $database_ok = FALSE;
- $pdo_message = t('Your web server does not appear to support any common PDO database extensions. Check with your hosting provider to see if they support PDO (PHP Data Objects) and offer any databases that <a href=":drupal-databases">Drupal supports</a>.', [
- ':drupal-databases' => 'https://www.drupal.org/docs/system-requirements/database-server-requirements',
- ]);
- }
- // Make sure the native PDO extension is available, not the older PEAR
- // version. (See install_verify_pdo() for details.)
- if (!defined('PDO::ATTR_DEFAULT_FETCH_MODE')) {
- $database_ok = FALSE;
- $pdo_message = t('Your web server seems to have the wrong version of PDO installed. Drupal requires the PDO extension from PHP core. This system has the older PECL version. See the <a href=":link">system requirements</a> page for more information.', [
- ':link' => 'https://www.drupal.org/docs/system-requirements/php-requirements#database',
- ]);
- }
- }
-
- if (!$database_ok) {
- $requirements['database_extensions']['value'] = t('Disabled');
- $requirements['database_extensions']['severity'] = REQUIREMENT_ERROR;
- $requirements['database_extensions']['description'] = $pdo_message;
- }
- else {
- $requirements['database_extensions']['value'] = t('Enabled');
- }
- }
-
- if ($phase === 'runtime' || $phase === 'update') {
- // Database information.
- $class = Database::getConnection()->getConnectionOptions()['namespace'] . '\\Install\\Tasks';
- /** @var \Drupal\Core\Database\Install\Tasks $tasks */
- $tasks = new $class();
- $requirements['database_system'] = [
- 'title' => t('Database system'),
- 'value' => $tasks->name(),
- ];
- $requirements['database_system_version'] = [
- 'title' => t('Database system version'),
- 'value' => Database::getConnection()->version(),
- ];
-
- $errors = $tasks->engineVersionRequirementsCheck();
- $error_count = count($errors);
- if ($error_count > 0) {
- $error_message = [
- '#theme' => 'item_list',
- '#items' => $errors,
- // Use the comma-list style to display a single error without bullets.
- '#context' => ['list_style' => $error_count === 1 ? 'comma-list' : ''],
- ];
- $requirements['database_system_version']['severity'] = REQUIREMENT_ERROR;
- $requirements['database_system_version']['description'] = $error_message;
- }
- }
-
- if ($phase === 'runtime' || $phase === 'update') {
- // Test database JSON support.
- $requirements['database_support_json'] = [
- 'title' => t('Database support for JSON'),
- 'severity' => REQUIREMENT_OK,
- 'value' => t('Available'),
- 'description' => t('Drupal requires databases that support JSON storage.'),
- ];
-
- if (!Database::getConnection()->hasJson()) {
- $requirements['database_support_json']['value'] = t('Not available');
- $requirements['database_support_json']['severity'] = REQUIREMENT_ERROR;
- }
- }
-
- // Test PHP memory_limit
- $memory_limit = ini_get('memory_limit');
- $requirements['php_memory_limit'] = [
- 'title' => t('PHP memory limit'),
- 'value' => $memory_limit == -1 ? t('-1 (Unlimited)') : $memory_limit,
- ];
-
- if (!Environment::checkMemoryLimit(\Drupal::MINIMUM_PHP_MEMORY_LIMIT, $memory_limit)) {
- $description = [];
- if ($phase == 'install') {
- $description['phase'] = t('Consider increasing your PHP memory limit to %memory_minimum_limit to help prevent errors in the installation process.', ['%memory_minimum_limit' => \Drupal::MINIMUM_PHP_MEMORY_LIMIT]);
- }
- elseif ($phase == 'update') {
- $description['phase'] = t('Consider increasing your PHP memory limit to %memory_minimum_limit to help prevent errors in the update process.', ['%memory_minimum_limit' => \Drupal::MINIMUM_PHP_MEMORY_LIMIT]);
- }
- elseif ($phase == 'runtime') {
- $description['phase'] = t('Depending on your configuration, Drupal can run with a %memory_limit PHP memory limit. However, a %memory_minimum_limit PHP memory limit or above is recommended, especially if your site uses additional custom or contributed modules.', ['%memory_limit' => $memory_limit, '%memory_minimum_limit' => \Drupal::MINIMUM_PHP_MEMORY_LIMIT]);
- }
-
- if (!empty($description['phase'])) {
- if ($php_ini_path = get_cfg_var('cfg_file_path')) {
- $description['memory'] = t('Increase the memory limit by editing the memory_limit parameter in the file %configuration-file and then restart your web server (or contact your system administrator or hosting provider for assistance).', ['%configuration-file' => $php_ini_path]);
- }
- else {
- $description['memory'] = t('Contact your system administrator or hosting provider for assistance with increasing your PHP memory limit.');
- }
-
- $handbook_link = t('For more information, see the online handbook entry for <a href=":memory-limit">increasing the PHP memory limit</a>.', [':memory-limit' => 'https://www.drupal.org/node/207036']);
-
- $description = [
- '#type' => 'inline_template',
- '#template' => '{{ description_phase }} {{ description_memory }} {{ handbook }}',
- '#context' => [
- 'description_phase' => $description['phase'],
- 'description_memory' => $description['memory'],
- 'handbook' => $handbook_link,
- ],
- ];
-
- $requirements['php_memory_limit']['description'] = $description;
- $requirements['php_memory_limit']['severity'] = REQUIREMENT_WARNING;
- }
- }
-
- // Test if configuration files and directory are writable.
- if ($phase == 'runtime') {
- $conf_errors = [];
- // Find the site path. Kernel service is not always available at this point,
- // but is preferred, when available.
- if (\Drupal::hasService('kernel')) {
- $site_path = \Drupal::getContainer()->getParameter('site.path');
- }
- else {
- $site_path = DrupalKernel::findSitePath(Request::createFromGlobals());
- }
- // Allow system administrators to disable permissions hardening for the site
- // directory. This allows additional files in the site directory to be
- // updated when they are managed in a version control system.
- if (Settings::get('skip_permissions_hardening')) {
- $error_value = t('Protection disabled');
- // If permissions hardening is disabled, then only show a warning for a
- // writable file, as a reminder, rather than an error.
- $file_protection_severity = REQUIREMENT_WARNING;
- }
- else {
- $error_value = t('Not protected');
- // In normal operation, writable files or directories are an error.
- $file_protection_severity = REQUIREMENT_ERROR;
- if (!drupal_verify_install_file($site_path, FILE_NOT_WRITABLE, 'dir')) {
- $conf_errors[] = t("The directory %file is not protected from modifications and poses a security risk. You must change the directory's permissions to be non-writable.", ['%file' => $site_path]);
- }
- }
- foreach (['settings.php', 'settings.local.php', 'services.yml'] as $conf_file) {
- $full_path = $site_path . '/' . $conf_file;
- if (file_exists($full_path) && !drupal_verify_install_file($full_path, FILE_EXIST | FILE_READABLE | FILE_NOT_WRITABLE, 'file', !Settings::get('skip_permissions_hardening'))) {
- $conf_errors[] = t("The file %file is not protected from modifications and poses a security risk. You must change the file's permissions to be non-writable.", ['%file' => $full_path]);
- }
- }
- if (!empty($conf_errors)) {
- if (count($conf_errors) == 1) {
- $description = $conf_errors[0];
- }
- else {
- // We use twig inline_template to avoid double escaping.
- $description = [
- '#type' => 'inline_template',
- '#template' => '{{ configuration_error_list }}',
- '#context' => [
- 'configuration_error_list' => [
- '#theme' => 'item_list',
- '#items' => $conf_errors,
- ],
- ],
- ];
- }
- $requirements['configuration_files'] = [
- 'value' => $error_value,
- 'severity' => $file_protection_severity,
- 'description' => $description,
- ];
- }
- else {
- $requirements['configuration_files'] = [
- 'value' => t('Protected'),
- ];
- }
- $requirements['configuration_files']['title'] = t('Configuration files');
- }
-
- // Test the contents of the .htaccess files.
- if ($phase == 'runtime') {
- // Try to write the .htaccess files first, to prevent false alarms in case
- // (for example) the /tmp directory was wiped.
- /** @var \Drupal\Core\File\HtaccessWriterInterface $htaccessWriter */
- $htaccessWriter = \Drupal::service("file.htaccess_writer");
- $htaccessWriter->ensure();
- foreach ($htaccessWriter->defaultProtectedDirs() as $protected_dir) {
- $htaccess_file = $protected_dir->getPath() . '/.htaccess';
- // Check for the string which was added to the recommended .htaccess file
- // in the latest security update.
- if (!file_exists($htaccess_file) || !($contents = @file_get_contents($htaccess_file)) || !str_contains($contents, 'Drupal_Security_Do_Not_Remove_See_SA_2013_003')) {
- $url = 'https://www.drupal.org/SA-CORE-2013-003';
- $requirements[$htaccess_file] = [
- // phpcs:ignore Drupal.Semantics.FunctionT.NotLiteralString
- 'title' => new TranslatableMarkup($protected_dir->getTitle()),
- 'value' => t('Not fully protected'),
- 'severity' => REQUIREMENT_ERROR,
- 'description' => t('See <a href=":url">@url</a> for information about the recommended .htaccess file which should be added to the %directory directory to help protect against arbitrary code execution.', [':url' => $url, '@url' => $url, '%directory' => $protected_dir->getPath()]),
- ];
- }
- }
- }
-
- // Report cron status.
- if ($phase == 'runtime') {
- $cron_config = \Drupal::config('system.cron');
- // Cron warning threshold defaults to two days.
- $threshold_warning = $cron_config->get('threshold.requirements_warning');
- // Cron error threshold defaults to two weeks.
- $threshold_error = $cron_config->get('threshold.requirements_error');
-
- // Determine when cron last ran.
- $cron_last = \Drupal::state()->get('system.cron_last');
- if (!is_numeric($cron_last)) {
- $cron_last = \Drupal::state()->get('install_time', 0);
- }
-
- // Determine severity based on time since cron last ran.
- $severity = REQUIREMENT_INFO;
- $request_time = \Drupal::time()->getRequestTime();
- if ($request_time - $cron_last > $threshold_error) {
- $severity = REQUIREMENT_ERROR;
- }
- elseif ($request_time - $cron_last > $threshold_warning) {
- $severity = REQUIREMENT_WARNING;
- }
-
- // Set summary and description based on values determined above.
- $summary = t('Last run @time ago', ['@time' => \Drupal::service('date.formatter')->formatTimeDiffSince($cron_last)]);
-
- $requirements['cron'] = [
- 'title' => t('Cron maintenance tasks'),
- 'severity' => $severity,
- 'value' => $summary,
- ];
- if ($severity != REQUIREMENT_INFO) {
- $requirements['cron']['description'][] = [
- [
- '#markup' => t('Cron has not run recently.'),
- '#suffix' => ' ',
- ],
- [
- '#markup' => t('For more information, see the online handbook entry for <a href=":cron-handbook">configuring cron jobs</a>.', [':cron-handbook' => 'https://www.drupal.org/docs/administering-a-drupal-site/cron-automated-tasks/cron-automated-tasks-overview']),
- '#suffix' => ' ',
- ],
- ];
- }
- $requirements['cron']['description'][] = [
- [
- '#type' => 'link',
- '#prefix' => '(',
- '#title' => t('more information'),
- '#suffix' => ')',
- '#url' => Url::fromRoute('system.cron_settings'),
- ],
- [
- '#prefix' => '<span class="cron-description__run-cron">',
- '#suffix' => '</span>',
- '#type' => 'link',
- '#title' => t('Run cron'),
- '#url' => Url::fromRoute('system.run_cron'),
- ],
- ];
- }
- if ($phase != 'install') {
- $directories = [
- PublicStream::basePath(),
- // By default no private files directory is configured. For private files
- // to be secure the admin needs to provide a path outside the webroot.
- PrivateStream::basePath(),
- \Drupal::service('file_system')->getTempDirectory(),
- ];
- }
-
- // During an install we need to make assumptions about the file system
- // unless overrides are provided in settings.php.
- if ($phase == 'install') {
- $directories = [];
- if ($file_public_path = Settings::get('file_public_path')) {
- $directories[] = $file_public_path;
- }
- else {
- // If we are installing Drupal, the settings.php file might not exist yet
- // in the intended site directory, so don't require it.
- $request = Request::createFromGlobals();
- $site_path = DrupalKernel::findSitePath($request);
- $directories[] = $site_path . '/files';
- }
- if ($file_private_path = Settings::get('file_private_path')) {
- $directories[] = $file_private_path;
- }
- if (Settings::get('file_temp_path')) {
- $directories[] = Settings::get('file_temp_path');
- }
- else {
- // If the temporary directory is not overridden use an appropriate
- // temporary path for the system.
- $directories[] = FileSystemComponent::getOsTemporaryDirectory();
- }
- }
-
- // Check the config directory if it is defined in settings.php. If it isn't
- // defined, the installer will create a valid config directory later, but
- // during runtime we must always display an error.
- $config_sync_directory = Settings::get('config_sync_directory');
- if (!empty($config_sync_directory)) {
- // If we're installing Drupal try and create the config sync directory.
- if (!is_dir($config_sync_directory) && $phase == 'install') {
- \Drupal::service('file_system')->prepareDirectory($config_sync_directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
- }
- if (!is_dir($config_sync_directory)) {
- if ($phase == 'install') {
- $description = t('An automated attempt to create the directory %directory failed, possibly due to a permissions problem. To proceed with the installation, either create the directory and modify its permissions manually or ensure that the installer has the permissions to create it automatically. For more information, see INSTALL.txt or the <a href=":handbook_url">online handbook</a>.', ['%directory' => $config_sync_directory, ':handbook_url' => 'https://www.drupal.org/server-permissions']);
- }
- else {
- $description = t('The directory %directory does not exist.', ['%directory' => $config_sync_directory]);
- }
- $requirements['config sync directory'] = [
- 'title' => t('Configuration sync directory'),
- 'description' => $description,
- 'severity' => REQUIREMENT_ERROR,
- ];
- }
- }
- if ($phase != 'install' && empty($config_sync_directory)) {
- $requirements['config sync directory'] = [
- 'title' => t('Configuration sync directory'),
- 'value' => t('Not present'),
- 'description' => t("Your %file file must define the %setting setting as a string containing the directory in which configuration files can be found.", ['%file' => $site_path . '/settings.php', '%setting' => "\$settings['config_sync_directory']"]),
- 'severity' => REQUIREMENT_ERROR,
- ];
- }
-
- $requirements['file system'] = [
- 'title' => t('File system'),
- ];
-
- $error = '';
- // For installer, create the directories if possible.
- foreach ($directories as $directory) {
- if (!$directory) {
- continue;
- }
- if ($phase == 'install') {
- \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
- }
- $is_writable = is_writable($directory);
- $is_directory = is_dir($directory);
- if (!$is_writable || !$is_directory) {
- $description = '';
- $requirements['file system']['value'] = t('Not writable');
- if (!$is_directory) {
- $error = t('The directory %directory does not exist.', ['%directory' => $directory]);
- }
- else {
- $error = t('The directory %directory is not writable.', ['%directory' => $directory]);
- }
- // The files directory requirement check is done only during install and
- // runtime.
- if ($phase == 'runtime') {
- $description = t('You may need to set the correct directory at the <a href=":admin-file-system">file system settings page</a> or change the current directory\'s permissions so that it is writable.', [':admin-file-system' => Url::fromRoute('system.file_system_settings')->toString()]);
- }
- elseif ($phase == 'install') {
- // For the installer UI, we need different wording. 'value' will
- // be treated as version, so provide none there.
- $description = t('An automated attempt to create this directory failed, possibly due to a permissions problem. To proceed with the installation, either create the directory and modify its permissions manually or ensure that the installer has the permissions to create it automatically. For more information, see INSTALL.txt or the <a href=":handbook_url">online handbook</a>.', [':handbook_url' => 'https://www.drupal.org/server-permissions']);
- $requirements['file system']['value'] = '';
- }
- if (!empty($description)) {
- $description = [
- '#type' => 'inline_template',
- '#template' => '{{ error }} {{ description }}',
- '#context' => [
- 'error' => $error,
- 'description' => $description,
- ],
- ];
- $requirements['file system']['description'] = $description;
- $requirements['file system']['severity'] = REQUIREMENT_ERROR;
- }
- }
- else {
- // This function can be called before the config_cache table has been
- // created.
- if ($phase == 'install' || \Drupal::config('system.file')->get('default_scheme') == 'public') {
- $requirements['file system']['value'] = t('Writable (<em>public</em> download method)');
- }
- else {
- $requirements['file system']['value'] = t('Writable (<em>private</em> download method)');
- }
- }
- }
-
- // See if updates are available in update.php.
- if ($phase == 'runtime') {
- $requirements['update'] = [
- 'title' => t('Database updates'),
- 'value' => t('Up to date'),
- ];
-
- // Check installed modules.
- $has_pending_updates = FALSE;
- /** @var \Drupal\Core\Update\UpdateHookRegistry $update_registry */
- $update_registry = \Drupal::service('update.update_hook_registry');
- foreach (\Drupal::moduleHandler()->getModuleList() as $module => $filename) {
- $updates = $update_registry->getAvailableUpdates($module);
- if ($updates) {
- $default = $update_registry->getInstalledVersion($module);
- if (max($updates) > $default) {
- $has_pending_updates = TRUE;
- break;
- }
- }
- }
- if (!$has_pending_updates) {
- /** @var \Drupal\Core\Update\UpdateRegistry $post_update_registry */
- $post_update_registry = \Drupal::service('update.post_update_registry');
- $missing_post_update_functions = $post_update_registry->getPendingUpdateFunctions();
- if (!empty($missing_post_update_functions)) {
- $has_pending_updates = TRUE;
- }
- }
-
- if ($has_pending_updates) {
- $requirements['update']['severity'] = REQUIREMENT_ERROR;
- $requirements['update']['value'] = t('Out of date');
- $requirements['update']['description'] = t('Some modules have database schema updates to install. You should run the <a href=":update">database update script</a> immediately.', [':update' => Url::fromRoute('system.db_update')->toString()]);
- }
-
- $requirements['entity_update'] = [
- 'title' => t('Entity/field definitions'),
- 'value' => t('Up to date'),
- ];
- // Verify that no entity updates are pending.
- if ($change_list = \Drupal::entityDefinitionUpdateManager()->getChangeSummary()) {
- $build = [];
- foreach ($change_list as $entity_type_id => $changes) {
- $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
- $build[] = [
- '#theme' => 'item_list',
- '#title' => $entity_type->getLabel(),
- '#items' => $changes,
- ];
- }
-
- $entity_update_issues = \Drupal::service('renderer')->renderInIsolation($build);
- $requirements['entity_update']['severity'] = REQUIREMENT_ERROR;
- $requirements['entity_update']['value'] = t('Mismatched entity and/or field definitions');
- $requirements['entity_update']['description'] = t('The following changes were detected in the entity type and field definitions. @updates', ['@updates' => $entity_update_issues]);
- }
- }
-
- // Display the deployment identifier if set.
- if ($phase == 'runtime') {
- if ($deployment_identifier = Settings::get('deployment_identifier')) {
- $requirements['deployment identifier'] = [
- 'title' => t('Deployment identifier'),
- 'value' => $deployment_identifier,
- 'severity' => REQUIREMENT_INFO,
- ];
- }
- }
-
- // Verify the update.php access setting
- if ($phase == 'runtime') {
- if (Settings::get('update_free_access')) {
- $requirements['update access'] = [
- 'value' => t('Not protected'),
- 'severity' => REQUIREMENT_ERROR,
- 'description' => t('The update.php script is accessible to everyone without authentication check, which is a security risk. You must change the @settings_name value in your settings.php back to FALSE.', ['@settings_name' => '$settings[\'update_free_access\']']),
- ];
- }
- else {
- $requirements['update access'] = [
- 'value' => t('Protected'),
- ];
- }
- $requirements['update access']['title'] = t('Access to update.php');
- }
-
- // Display an error if a newly introduced dependency in a module is not
- // resolved.
- if ($phase === 'update' || $phase === 'runtime') {
- $create_extension_incompatibility_list = function (array $extension_names, PluralTranslatableMarkup $description, PluralTranslatableMarkup $title, TranslatableMarkup|string $message = '', TranslatableMarkup|string $additional_description = '') {
- if ($message === '') {
- $message = new TranslatableMarkup('Review the <a href=":url"> suggestions for resolving this incompatibility</a> to repair your installation, and then re-run update.php.', [':url' => 'https://www.drupal.org/docs/updating-drupal/troubleshooting-database-updates']);
- }
- // Use an inline twig template to:
- // - Concatenate MarkupInterface objects and preserve safeness.
- // - Use the item_list theme for the extension list.
- $template = [
- '#type' => 'inline_template',
- '#template' => '{{ description }}{{ extensions }}{{ additional_description }}<br>',
- '#context' => [
- 'extensions' => [
- '#theme' => 'item_list',
- ],
- ],
- ];
- $template['#context']['extensions']['#items'] = $extension_names;
- $template['#context']['description'] = $description;
- $template['#context']['additional_description'] = $additional_description;
- return [
- 'title' => $title,
- 'value' => [
- 'list' => $template,
- 'handbook_link' => [
- '#markup' => $message,
- ],
- ],
- 'severity' => REQUIREMENT_ERROR,
- ];
- };
- $profile = \Drupal::installProfile();
- $files = $module_extension_list->getList();
- $files += $theme_extension_list->getList();
- $core_incompatible_extensions = [];
- $php_incompatible_extensions = [];
- foreach ($files as $extension_name => $file) {
- // Ignore uninstalled extensions and installation profiles.
- if (!$file->status || $extension_name == $profile) {
- continue;
- }
-
- $name = $file->info['name'];
- if (!empty($file->info['core_incompatible'])) {
- $core_incompatible_extensions[$file->info['type']][] = $name;
- }
-
- // Check the extension's PHP version.
- $php = $file->info['php'];
- if (version_compare($php, PHP_VERSION, '>')) {
- $php_incompatible_extensions[$file->info['type']][] = $name;
- }
-
- // Check the module's required modules.
- /** @var \Drupal\Core\Extension\Dependency $requirement */
- foreach ($file->requires as $requirement) {
- $required_module = $requirement->getName();
- // Check if the module exists.
- if (!isset($files[$required_module])) {
- $requirements["$extension_name-$required_module"] = [
- 'title' => t('Unresolved dependency'),
- 'description' => t('@name requires this module.', ['@name' => $name]),
- 'value' => t('@required_name (Missing)', ['@required_name' => $required_module]),
- 'severity' => REQUIREMENT_ERROR,
- ];
- continue;
- }
- // Check for an incompatible version.
- $required_file = $files[$required_module];
- $required_name = $required_file->info['name'];
- // Remove CORE_COMPATIBILITY- only from the start of the string.
- $version = preg_replace('/^(' . \Drupal::CORE_COMPATIBILITY . '\-)/', '', $required_file->info['version'] ?? '');
- if (!$requirement->isCompatible($version)) {
- $requirements["$extension_name-$required_module"] = [
- 'title' => t('Unresolved dependency'),
- 'description' => t('@name requires this module and version. Currently using @required_name version @version', ['@name' => $name, '@required_name' => $required_name, '@version' => $version]),
- 'value' => t('@required_name (Version @compatibility required)', ['@required_name' => $required_name, '@compatibility' => $requirement->getConstraintString()]),
- 'severity' => REQUIREMENT_ERROR,
- ];
- continue;
- }
- }
- }
- if (!empty($core_incompatible_extensions['module'])) {
- $requirements['module_core_incompatible'] = $create_extension_incompatibility_list(
- $core_incompatible_extensions['module'],
- new PluralTranslatableMarkup(
- count($core_incompatible_extensions['module']),
- 'The following module is installed, but it is incompatible with Drupal @version:',
- 'The following modules are installed, but they are incompatible with Drupal @version:',
- ['@version' => \Drupal::VERSION]
- ),
- new PluralTranslatableMarkup(
- count($core_incompatible_extensions['module']),
- 'Incompatible module',
- 'Incompatible modules'
- )
- );
- }
- if (!empty($core_incompatible_extensions['theme'])) {
- $requirements['theme_core_incompatible'] = $create_extension_incompatibility_list(
- $core_incompatible_extensions['theme'],
- new PluralTranslatableMarkup(
- count($core_incompatible_extensions['theme']),
- 'The following theme is installed, but it is incompatible with Drupal @version:',
- 'The following themes are installed, but they are incompatible with Drupal @version:',
- ['@version' => \Drupal::VERSION]
- ),
- new PluralTranslatableMarkup(
- count($core_incompatible_extensions['theme']),
- 'Incompatible theme',
- 'Incompatible themes'
- )
- );
- }
- if (!empty($php_incompatible_extensions['module'])) {
- $requirements['module_php_incompatible'] = $create_extension_incompatibility_list(
- $php_incompatible_extensions['module'],
- new PluralTranslatableMarkup(
- count($php_incompatible_extensions['module']),
- 'The following module is installed, but it is incompatible with PHP @version:',
- 'The following modules are installed, but they are incompatible with PHP @version:',
- ['@version' => phpversion()]
- ),
- new PluralTranslatableMarkup(
- count($php_incompatible_extensions['module']),
- 'Incompatible module',
- 'Incompatible modules'
- )
- );
- }
- if (!empty($php_incompatible_extensions['theme'])) {
- $requirements['theme_php_incompatible'] = $create_extension_incompatibility_list(
- $php_incompatible_extensions['theme'],
- new PluralTranslatableMarkup(
- count($php_incompatible_extensions['theme']),
- 'The following theme is installed, but it is incompatible with PHP @version:',
- 'The following themes are installed, but they are incompatible with PHP @version:',
- ['@version' => phpversion()]
- ),
- new PluralTranslatableMarkup(
- count($php_incompatible_extensions['theme']),
- 'Incompatible theme',
- 'Incompatible themes'
- )
- );
- }
-
- $extension_config = \Drupal::configFactory()->get('core.extension');
-
- // Look for removed core modules.
- $is_removed_module = function ($extension_name) use ($module_extension_list) {
- return !$module_extension_list->exists($extension_name)
- && array_key_exists($extension_name, DRUPAL_CORE_REMOVED_MODULE_LIST);
- };
- $removed_modules = array_filter(array_keys($extension_config->get('module')), $is_removed_module);
- if (!empty($removed_modules)) {
- $list = [];
- foreach ($removed_modules as $removed_module) {
- $list[] = t('<a href=":url">@module</a>', [
- ':url' => "https://www.drupal.org/project/$removed_module",
- '@module' => DRUPAL_CORE_REMOVED_MODULE_LIST[$removed_module],
- ]);
- }
- $requirements['removed_module'] = $create_extension_incompatibility_list(
- $list,
- new PluralTranslatableMarkup(
- count($removed_modules),
- 'You must add the following contributed module and reload this page.',
- 'You must add the following contributed modules and reload this page.'
- ),
- new PluralTranslatableMarkup(
- count($removed_modules),
- 'Removed core module',
- 'Removed core modules'
- ),
- new TranslatableMarkup(
- 'For more information read the <a href=":url">documentation on deprecated modules.</a>',
- [':url' => 'https://www.drupal.org/node/3223395#s-recommendations-for-deprecated-modules']
- ),
- new PluralTranslatableMarkup(
- count($removed_modules),
- 'This module is installed on your site but is no longer provided by Core.',
- 'These modules are installed on your site but are no longer provided by Core.'
- ),
- );
- }
-
- // Look for removed core themes.
- $is_removed_theme = function ($extension_name) use ($theme_extension_list) {
- return !$theme_extension_list->exists($extension_name)
- && array_key_exists($extension_name, DRUPAL_CORE_REMOVED_THEME_LIST);
- };
- $removed_themes = array_filter(array_keys($extension_config->get('theme')), $is_removed_theme);
- if (!empty($removed_themes)) {
- $list = [];
- foreach ($removed_themes as $removed_theme) {
- $list[] = t('<a href=":url">@theme</a>', [
- ':url' => "https://www.drupal.org/project/$removed_theme",
- '@theme' => DRUPAL_CORE_REMOVED_THEME_LIST[$removed_theme],
- ]);
- }
- $requirements['removed_theme'] = $create_extension_incompatibility_list(
- $list,
- new PluralTranslatableMarkup(
- count($removed_themes),
- 'You must add the following contributed theme and reload this page.',
- 'You must add the following contributed themes and reload this page.'
- ),
- new PluralTranslatableMarkup(
- count($removed_themes),
- 'Removed core theme',
- 'Removed core themes'
- ),
- new TranslatableMarkup(
- 'For more information read the <a href=":url">documentation on deprecated themes.</a>',
- [':url' => 'https://www.drupal.org/node/3223395#s-recommendations-for-deprecated-themes']
- ),
- new PluralTranslatableMarkup(
- count($removed_themes),
- 'This theme is installed on your site but is no longer provided by Core.',
- 'These themes are installed on your site but are no longer provided by Core.'
- ),
- );
- }
-
- // Look for missing modules.
- $is_missing_module = function ($extension_name) use ($module_extension_list) {
- return !$module_extension_list->exists($extension_name) && !in_array($extension_name, array_keys(DRUPAL_CORE_REMOVED_MODULE_LIST), TRUE);
- };
- $invalid_modules = array_filter(array_keys($extension_config->get('module')), $is_missing_module);
-
- if (!empty($invalid_modules)) {
- $requirements['invalid_module'] = $create_extension_incompatibility_list(
- $invalid_modules,
- new PluralTranslatableMarkup(
- count($invalid_modules),
- 'The following module is marked as installed in the core.extension configuration, but it is missing:',
- 'The following modules are marked as installed in the core.extension configuration, but they are missing:'
- ),
- new PluralTranslatableMarkup(
- count($invalid_modules),
- 'Missing or invalid module',
- 'Missing or invalid modules'
- )
- );
- }
-
- // Look for invalid themes.
- $is_missing_theme = function ($extension_name) use (&$theme_extension_list) {
- return !$theme_extension_list->exists($extension_name) && !in_array($extension_name, array_keys(DRUPAL_CORE_REMOVED_THEME_LIST), TRUE);
- };
- $invalid_themes = array_filter(array_keys($extension_config->get('theme')), $is_missing_theme);
- if (!empty($invalid_themes)) {
- $requirements['invalid_theme'] = $create_extension_incompatibility_list(
- $invalid_themes,
- new PluralTranslatableMarkup(
- count($invalid_themes),
- 'The following theme is marked as installed in the core.extension configuration, but it is missing:',
- 'The following themes are marked as installed in the core.extension configuration, but they are missing:'
- ),
- new PluralTranslatableMarkup(
- count($invalid_themes),
- 'Missing or invalid theme',
- 'Missing or invalid themes'
- )
- );
- }
- }
-
- // Returns Unicode library status and errors.
- $libraries = [
- Unicode::STATUS_SINGLEBYTE => t('Standard PHP'),
- Unicode::STATUS_MULTIBYTE => t('PHP Mbstring Extension'),
- Unicode::STATUS_ERROR => t('Error'),
- ];
- $severities = [
- Unicode::STATUS_SINGLEBYTE => REQUIREMENT_WARNING,
- Unicode::STATUS_MULTIBYTE => NULL,
- Unicode::STATUS_ERROR => REQUIREMENT_ERROR,
- ];
- $failed_check = Unicode::check();
- $library = Unicode::getStatus();
-
- $requirements['unicode'] = [
- 'title' => t('Unicode library'),
- 'value' => $libraries[$library],
- 'severity' => $severities[$library],
- ];
- switch ($failed_check) {
- case 'mb_strlen':
- $requirements['unicode']['description'] = t('Operations on Unicode strings are emulated on a best-effort basis. Install the <a href="http://php.net/mbstring">PHP mbstring extension</a> for improved Unicode support.');
- break;
-
- case 'mbstring.encoding_translation':
- $requirements['unicode']['description'] = t('Multibyte string input conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.encoding_translation</em> setting. Refer to the <a href="http://php.net/mbstring">PHP mbstring documentation</a> for more information.');
- break;
- }
-
- if ($phase == 'runtime') {
- // Check for update status module.
- if (!\Drupal::moduleHandler()->moduleExists('update')) {
- $requirements['update status'] = [
- 'value' => t('Not enabled'),
- 'severity' => REQUIREMENT_WARNING,
- 'description' => t('Update notifications are not enabled. It is <strong>highly recommended</strong> that you install the Update Status module from the <a href=":module">module administration page</a> in order to stay up-to-date on new releases. For more information, <a href=":update">Update status handbook page</a>.', [
- ':update' => 'https://www.drupal.org/documentation/modules/update',
- ':module' => Url::fromRoute('system.modules_list')->toString(),
- ]),
- ];
- }
- else {
- $requirements['update status'] = [
- 'value' => t('Enabled'),
- ];
- }
- $requirements['update status']['title'] = t('Update notifications');
-
- if (Settings::get('rebuild_access')) {
- $requirements['rebuild access'] = [
- 'title' => t('Rebuild access'),
- 'value' => t('Enabled'),
- 'severity' => REQUIREMENT_ERROR,
- 'description' => t('The rebuild_access setting is enabled in settings.php. It is recommended to have this setting disabled unless you are performing a rebuild.'),
- ];
- }
- }
-
- // Check if the SameSite cookie attribute is set to a valid value. Since this
- // involves checking whether we are using a secure connection this only makes
- // sense inside an HTTP request, not on the command line.
- if ($phase === 'runtime' && PHP_SAPI !== 'cli') {
- $samesite = ini_get('session.cookie_samesite') ?: t('Not set');
- // Check if the SameSite attribute is set to a valid value. If it is set to
- // 'None' the request needs to be done over HTTPS.
- $valid = match ($samesite) {
- 'Lax', 'Strict' => TRUE,
- 'None' => $request_object->isSecure(),
- default => FALSE,
- };
- $requirements['php_session_samesite'] = [
- 'title' => t('SameSite cookie attribute'),
- 'value' => $samesite,
- 'severity' => $valid ? REQUIREMENT_OK : REQUIREMENT_WARNING,
- 'description' => t('This attribute should be explicitly set to Lax, Strict or None. If set to None then the request must be made via HTTPS. See <a href=":url" target="_blank">PHP documentation</a>', [
- ':url' => 'https://www.php.net/manual/en/session.configuration.php#ini.session.cookie-samesite',
- ]),
- ];
- }
-
- // See if trusted host names have been configured, and warn the user if they
- // are not set.
- if ($phase == 'runtime') {
- $trusted_host_patterns = Settings::get('trusted_host_patterns');
- if (empty($trusted_host_patterns)) {
- $requirements['trusted_host_patterns'] = [
- 'title' => t('Trusted Host Settings'),
- 'value' => t('Not enabled'),
- 'description' => t('The trusted_host_patterns setting is not configured in settings.php. This can lead to security vulnerabilities. It is <strong>highly recommended</strong> that you configure this. See <a href=":url">Protecting against HTTP HOST Header attacks</a> for more information.', [':url' => 'https://www.drupal.org/docs/installing-drupal/trusted-host-settings']),
- 'severity' => REQUIREMENT_ERROR,
- ];
- }
- else {
- $requirements['trusted_host_patterns'] = [
- 'title' => t('Trusted Host Settings'),
- 'value' => t('Enabled'),
- 'description' => t('The trusted_host_patterns setting is set to allow %trusted_host_patterns', ['%trusted_host_patterns' => implode(', ', $trusted_host_patterns)]),
- ];
- }
- }
-
- // When the database driver is provided by a module, then check that the
- // providing module is installed.
- if ($phase === 'runtime' || $phase === 'update') {
- $connection = Database::getConnection();
- $provider = $connection->getProvider();
- if ($provider !== 'core' && !\Drupal::moduleHandler()->moduleExists($provider)) {
- $autoload = $connection->getConnectionOptions()['autoload'] ?? '';
- if (str_contains($autoload, 'src/Driver/Database/')) {
- $post_update_registry = \Drupal::service('update.post_update_registry');
- $pending_updates = $post_update_registry->getPendingUpdateInformation();
- if (!in_array('enable_provider_database_driver', array_keys($pending_updates['system']['pending'] ?? []), TRUE)) {
- // Only show the warning when the post update function has run and
- // the module that is providing the database driver is not installed.
- $requirements['database_driver_provided_by_module'] = [
- 'title' => t('Database driver provided by module'),
- 'value' => t('Not installed'),
- 'description' => t('The current database driver is provided by the module: %module. The module is currently not installed. You should immediately <a href=":install">install</a> the module.', ['%module' => $provider, ':install' => Url::fromRoute('system.modules_list')->toString()]),
- 'severity' => REQUIREMENT_ERROR,
- ];
- }
- }
- }
- }
-
- // Check xdebug.max_nesting_level, as some pages will not work if it is too
- // low.
- if (extension_loaded('xdebug')) {
- // Setting this value to 256 was considered adequate on Xdebug 2.3
- // (see http://bugs.xdebug.org/bug_view_page.php?bug_id=00001100)
- $minimum_nesting_level = 256;
- $current_nesting_level = ini_get('xdebug.max_nesting_level');
-
- if ($current_nesting_level < $minimum_nesting_level) {
- $requirements['xdebug_max_nesting_level'] = [
- 'title' => t('Xdebug settings'),
- 'value' => t('xdebug.max_nesting_level is set to %value.', ['%value' => $current_nesting_level]),
- 'description' => t('Set <code>xdebug.max_nesting_level=@level</code> in your PHP configuration as some pages in your Drupal site will not work when this setting is too low.', ['@level' => $minimum_nesting_level]),
- 'severity' => REQUIREMENT_ERROR,
- ];
- }
- }
-
- // Installations on Windows can run into limitations with MAX_PATH if the
- // Drupal root directory is too deep in the filesystem. Generally this shows
- // up in cached Twig templates and other public files with long directory or
- // file names. There is no definite root directory depth below which Drupal is
- // guaranteed to function correctly on Windows. Since problems are likely
- // with more than 100 characters in the Drupal root path, show an error.
- if (str_starts_with(PHP_OS, 'WIN')) {
- $depth = strlen(realpath(DRUPAL_ROOT . '/' . PublicStream::basePath()));
- if ($depth > 120) {
- $requirements['max_path_on_windows'] = [
- 'title' => t('Windows installation depth'),
- 'description' => t('The public files directory path is %depth characters. Paths longer than 120 characters will cause problems on Windows.', ['%depth' => $depth]),
- 'severity' => REQUIREMENT_ERROR,
- ];
- }
- }
- // Check to see if dates will be limited to 1901-2038.
- if (PHP_INT_SIZE <= 4) {
- $requirements['limited_date_range'] = [
- 'title' => t('Limited date range'),
- 'value' => t('Your PHP installation has a limited date range.'),
- 'description' => t('You are running on a system where PHP is compiled or limited to using 32-bit integers. This will limit the range of dates and timestamps to the years 1901-2038. Read about the <a href=":url">limitations of 32-bit PHP</a>.', [':url' => 'https://www.drupal.org/docs/system-requirements/limitations-of-32-bit-php']),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
-
- // During installs from configuration don't support install profiles that
- // implement hook_install.
- if ($phase == 'install' && !empty($install_state['config_install_path'])) {
- $install_hook = $install_state['parameters']['profile'] . '_install';
- if (function_exists($install_hook)) {
- $requirements['config_install'] = [
- 'title' => t('Configuration install'),
- 'value' => $install_state['parameters']['profile'],
- 'description' => t('The selected profile has a hook_install() implementation and therefore can not be installed from configuration.'),
- 'severity' => REQUIREMENT_ERROR,
- ];
- }
- }
-
- if ($phase === 'runtime') {
- $settings = Settings::getAll();
- if (array_key_exists('install_profile', $settings)) {
- // The following message is only informational because not all site owners
- // have access to edit their settings.php as it may be controlled by their
- // hosting provider.
- $requirements['install_profile_in_settings'] = [
- 'title' => t('Install profile in settings'),
- 'value' => t("Drupal 9 no longer uses the \$settings['install_profile'] value in settings.php and it should be removed."),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
- }
-
- // Ensure that no module has a current schema version that is lower than the
- // one that was last removed.
- if ($phase == 'update') {
- $module_handler = \Drupal::moduleHandler();
- /** @var \Drupal\Core\Update\UpdateHookRegistry $update_registry */
- $update_registry = \Drupal::service('update.update_hook_registry');
- $module_list = [];
- // hook_update_last_removed() is a procedural hook hook because we
- // do not have classes loaded that would be needed.
- // Simply inlining the old hook mechanism is better than making
- // ModuleInstaller::invoke() public.
- foreach ($module_handler->getModuleList() as $module => $extension) {
- $function = $module . '_update_last_removed';
- if (function_exists($function)) {
- $last_removed = $function();
- if ($last_removed && $last_removed > $update_registry->getInstalledVersion($module)) {
-
- /** @var \Drupal\Core\Extension\Extension $module_info */
- $module_info = $module_extension_list->get($module);
- $module_list[$module] = [
- 'name' => $module_info->info['name'],
- 'last_removed' => $last_removed,
- 'installed_version' => $update_registry->getInstalledVersion($module),
- ];
- }
- }
- }
-
- // If user module is in the list then only show a specific message for
- // Drupal core.
- if (isset($module_list['user'])) {
- $requirements['user_update_last_removed'] = [
- 'title' => t('The version of Drupal you are trying to update from is too old'),
- 'description' => t('Updating to Drupal @current_major is only supported from Drupal version @required_min_version or higher. If you are trying to update from an older version, first update to the latest version of Drupal @previous_major. (<a href=":url">Drupal upgrade guide</a>)', [
- '@current_major' => 10,
- '@required_min_version' => '9.4.0',
- '@previous_major' => 9,
- ':url' => 'https://www.drupal.org/docs/upgrading-drupal/drupal-8-and-higher',
- ]),
- 'severity' => REQUIREMENT_ERROR,
- ];
- }
- else {
- foreach ($module_list as $module => $data) {
- $requirements[$module . '_update_last_removed'] = [
- 'title' => t('Unsupported schema version: @module', ['@module' => $data['name']]),
- 'description' => t('The installed version of the %module module is too old to update. Update to an intermediate version first (last removed version: @last_removed_version, installed version: @installed_version).', [
- '%module' => $data['name'],
- '@last_removed_version' => $data['last_removed'],
- '@installed_version' => $data['installed_version'],
- ]),
- 'severity' => REQUIREMENT_ERROR,
- ];
- }
- }
- // Also check post-updates. Only do this if we're not already showing an
- // error for hook_update_N().
- $missing_updates = [];
- if (empty($module_list)) {
- $existing_updates = \Drupal::service('keyvalue')->get('post_update')->get('existing_updates', []);
- $post_update_registry = \Drupal::service('update.post_update_registry');
- $modules = \Drupal::moduleHandler()->getModuleList();
- foreach ($modules as $module => $extension) {
- $module_info = $module_extension_list->get($module);
- $removed_post_updates = $post_update_registry->getRemovedPostUpdates($module);
- if ($missing_updates = array_diff(array_keys($removed_post_updates), $existing_updates)) {
- $versions = array_unique(array_intersect_key($removed_post_updates, array_flip($missing_updates)));
- $description = new PluralTranslatableMarkup(count($versions),
- 'The installed version of the %module module is too old to update. Update to a version prior to @versions first (missing updates: @missing_updates).',
- 'The installed version of the %module module is too old to update. Update first to a version prior to all of the following: @versions (missing updates: @missing_updates).',
- [
- '%module' => $module_info->info['name'],
- '@missing_updates' => implode(', ', $missing_updates),
- '@versions' => implode(', ', $versions),
- ]
- );
- $requirements[$module . '_post_update_removed'] = [
- 'title' => t('Missing updates for: @module', ['@module' => $module_info->info['name']]),
- 'description' => $description,
- 'severity' => REQUIREMENT_ERROR,
- ];
- }
- }
- }
-
- if (empty($missing_updates)) {
- foreach ($update_registry->getAllEquivalentUpdates() as $module => $equivalent_updates) {
- $module_info = $module_extension_list->get($module);
- foreach ($equivalent_updates as $future_update => $data) {
- $future_update_function_name = $module . '_update_' . $future_update;
- $ran_update_function_name = $module . '_update_' . $data['ran_update'];
- // If an update was marked as an equivalent by a previous update, and
- // both the previous update and the equivalent update are not found in
- // the current code base, prevent updating. This indicates a site
- // attempting to go 'backwards' in terms of database schema.
- // @see \Drupal\Core\Update\UpdateHookRegistry::markFutureUpdateEquivalent()
- if (!function_exists($ran_update_function_name) && !function_exists($future_update_function_name)) {
- // If the module is provided by core prepend helpful text as the
- // module does not exist in composer or Drupal.org.
- if (str_starts_with($module_info->getPathname(), 'core/')) {
- $future_version_string = 'Drupal Core ' . $data['future_version_string'];
- }
- else {
- $future_version_string = $data['future_version_string'];
- }
- $requirements[$module . '_equivalent_update_missing'] = [
- 'title' => t('Missing updates for: @module', ['@module' => $module_info->info['name']]),
- 'description' => t('The version of the %module module that you are attempting to update to is missing update @future_update (which was marked as an equivalent by @ran_update). Update to at least @future_version_string.', [
- '%module' => $module_info->info['name'],
- '@ran_update' => $data['ran_update'],
- '@future_update' => $future_update,
- '@future_version_string' => $future_version_string,
- ]),
- 'severity' => REQUIREMENT_ERROR,
- ];
- break;
- }
- }
- }
- }
- }
-
- // Add warning when twig debug option is enabled.
- if ($phase === 'runtime') {
- $development_settings = \Drupal::keyValue('development_settings');
- $twig_debug = $development_settings->get('twig_debug', FALSE);
- $twig_cache_disable = $development_settings->get('twig_cache_disable', FALSE);
- if ($twig_debug || $twig_cache_disable) {
- $requirements['twig_debug_enabled'] = [
- 'title' => t('Twig development mode'),
- 'value' => t('Twig development mode settings are turned on. Go to @link to disable them.', [
- '@link' => Link::createFromRoute(
- 'development settings page',
- 'system.development_settings',
- )->toString(),
- ]),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
- $render_cache_disabled = $development_settings->get('disable_rendered_output_cache_bins', FALSE);
- if ($render_cache_disabled) {
- $requirements['render_cache_disabled'] = [
- 'title' => t('Markup caching disabled'),
- 'value' => t('Render cache, dynamic page cache, and page cache are bypassed. Go to @link to enable them.', [
- '@link' => Link::createFromRoute(
- 'development settings page',
- 'system.development_settings',
- )->toString(),
- ]),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
- }
-
- return $requirements;
-}
-
-/**
* Implements hook_install().
*/
function system_install(): void {
@@ -1685,57 +105,6 @@ function system_update_11200(): void {
}
/**
- * Display requirements from security advisories.
- *
- * @param array[] $requirements
- * The requirements array as specified in hook_requirements().
- */
-function _system_advisories_requirements(array &$requirements): void {
- if (!\Drupal::config('system.advisories')->get('enabled')) {
- return;
- }
-
- /** @var \Drupal\system\SecurityAdvisories\SecurityAdvisoriesFetcher $fetcher */
- $fetcher = \Drupal::service('system.sa_fetcher');
- try {
- $advisories = $fetcher->getSecurityAdvisories(TRUE, 5);
- }
- catch (ClientExceptionInterface $exception) {
- $requirements['system_advisories']['title'] = t('Critical security announcements');
- $requirements['system_advisories']['severity'] = REQUIREMENT_WARNING;
- $requirements['system_advisories']['description'] = ['#theme' => 'system_security_advisories_fetch_error_message'];
- Error::logException(\Drupal::logger('system'), $exception, 'Failed to retrieve security advisory data.');
- return;
- }
-
- if (!empty($advisories)) {
- $advisory_links = [];
- $severity = REQUIREMENT_WARNING;
- foreach ($advisories as $advisory) {
- if (!$advisory->isPsa()) {
- $severity = REQUIREMENT_ERROR;
- }
- $advisory_links[] = new Link($advisory->getTitle(), Url::fromUri($advisory->getUrl()));
- }
- $requirements['system_advisories']['title'] = t('Critical security announcements');
- $requirements['system_advisories']['severity'] = $severity;
- $requirements['system_advisories']['description'] = [
- 'list' => [
- '#theme' => 'item_list',
- '#items' => $advisory_links,
- ],
- ];
- if (\Drupal::moduleHandler()->moduleExists('help')) {
- $requirements['system_advisories']['description']['help_link'] = Link::createFromRoute(
- 'What are critical security announcements?',
- 'help.page', ['name' => 'system'],
- ['fragment' => 'security-advisories']
- )->toRenderable();
- }
- }
-}
-
-/**
* Invalidate container because the module handler has changed.
*/
function system_update_11100(): void {
diff --git a/core/modules/system/system.libraries.yml b/core/modules/system/system.libraries.yml
index 03baf83d3bb..cd7165bffbb 100644
--- a/core/modules/system/system.libraries.yml
+++ b/core/modules/system/system.libraries.yml
@@ -7,10 +7,7 @@ base:
css/components/container-inline.module.css: { weight: -10 }
css/components/clearfix.module.css: { weight: -10 }
css/components/hidden.module.css: { weight: -10 }
- css/components/item-list.module.css: { weight: -10 }
css/components/js.module.css: { weight: -10 }
- css/components/position-container.module.css: { weight: -10 }
- css/components/reset-appearance.module.css: { weight: -10 }
admin:
version: VERSION
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index dbda1e1b6b4..8fac07911e9 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -9,7 +9,6 @@ use Drupal\Core\Extension\Extension;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\RedirectResponse;
-use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
/**
* Disabled option on forms and settings.
@@ -61,81 +60,6 @@ function system_hook_info(): array {
}
/**
- * Implements hook_theme_suggestions_HOOK().
- */
-function system_theme_suggestions_html(array $variables): array {
- $path_args = explode('/', trim(\Drupal::service('path.current')->getPath(), '/'));
- return theme_get_suggestions($path_args, 'html');
-}
-
-/**
- * Implements hook_theme_suggestions_HOOK().
- */
-function system_theme_suggestions_page(array $variables): array {
- $path_args = explode('/', trim(\Drupal::service('path.current')->getPath(), '/'));
- $suggestions = theme_get_suggestions($path_args, 'page');
-
- $supported_http_error_codes = [401, 403, 404];
- $exception = \Drupal::requestStack()->getCurrentRequest()->attributes->get('exception');
- if ($exception instanceof HttpExceptionInterface && in_array($exception->getStatusCode(), $supported_http_error_codes, TRUE)) {
- $suggestions[] = 'page__4xx';
- $suggestions[] = 'page__' . $exception->getStatusCode();
- }
-
- return $suggestions;
-}
-
-/**
- * Implements hook_theme_suggestions_HOOK().
- */
-function system_theme_suggestions_maintenance_page(array $variables): array {
- $suggestions = [];
-
- // Dead databases will show error messages so supplying this template will
- // allow themers to override the page and the content completely.
- $offline = defined('MAINTENANCE_MODE');
- try {
- \Drupal::service('path.matcher')->isFrontPage();
- }
- catch (Exception) {
- // The database is not yet available.
- $offline = TRUE;
- }
- if ($offline) {
- $suggestions[] = 'maintenance_page__offline';
- }
-
- return $suggestions;
-}
-
-/**
- * Implements hook_theme_suggestions_HOOK().
- */
-function system_theme_suggestions_region(array $variables): array {
- $suggestions = [];
- if (!empty($variables['elements']['#region'])) {
- $suggestions[] = 'region__' . $variables['elements']['#region'];
- }
- return $suggestions;
-}
-
-/**
- * Implements hook_theme_suggestions_HOOK().
- */
-function system_theme_suggestions_field(array $variables): array {
- $suggestions = [];
- $element = $variables['element'];
-
- $suggestions[] = 'field__' . $element['#field_type'];
- $suggestions[] = 'field__' . $element['#field_name'];
- $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#bundle'];
- $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'];
- $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'] . '__' . $element['#bundle'];
-
- return $suggestions;
-}
-
-/**
* Prepares variables for the list of available bundles.
*
* Default template: entity-add-list.html.twig.
@@ -307,30 +231,6 @@ function system_authorized_batch_process() {
}
/**
- * Implements hook_preprocess_HOOK() for block templates.
- */
-function system_preprocess_block(&$variables): void {
- switch ($variables['base_plugin_id']) {
- case 'system_branding_block':
- $variables['site_logo'] = '';
- if ($variables['content']['site_logo']['#access'] && $variables['content']['site_logo']['#uri']) {
- $variables['site_logo'] = $variables['content']['site_logo']['#uri'];
- }
- $variables['site_name'] = '';
- if ($variables['content']['site_name']['#access'] && $variables['content']['site_name']['#markup']) {
- $variables['site_name'] = $variables['content']['site_name']['#markup'];
- }
- $variables['site_slogan'] = '';
- if ($variables['content']['site_slogan']['#access'] && $variables['content']['site_slogan']['#markup']) {
- $variables['site_slogan'] = [
- '#markup' => $variables['content']['site_slogan']['#markup'],
- ];
- }
- break;
- }
-}
-
-/**
* Checks the existence of the directory specified in $form_element.
*
* This function is called from the system_settings form to check all core
@@ -474,21 +374,3 @@ function _system_is_claro_admin_and_not_active() {
$active_theme = \Drupal::theme()->getActiveTheme()->getName();
return $active_theme !== 'claro' && $admin_theme === 'claro';
}
-
-/**
- * Implements hook_preprocess_toolbar().
- */
-function system_preprocess_toolbar(array &$variables, $hook, $info): void {
- // When Claro is the admin theme, Claro overrides the active theme's if that
- // active theme is not Claro. Because of these potential overrides, the
- // toolbar cache should be invalidated any time the default or admin theme
- // changes.
- $variables['#cache']['tags'][] = 'config:system.theme';
-
- // If Claro is the admin theme but not the active theme, still include Claro's
- // toolbar preprocessing.
- if (_system_is_claro_admin_and_not_active()) {
- require_once DRUPAL_ROOT . '/core/themes/claro/claro.theme';
- claro_preprocess_toolbar($variables, $hook, $info);
- }
-}
diff --git a/core/modules/system/templates/details.html.twig b/core/modules/system/templates/details.html.twig
index 20e4ea7193e..dcb1cf354ce 100644
--- a/core/modules/system/templates/details.html.twig
+++ b/core/modules/system/templates/details.html.twig
@@ -34,7 +34,11 @@
</div>
{% endif %}
- {{ description }}
+ {%- if description -%}
+ {% set description_attributes = create_attribute({id: attributes['aria-describedby']}) %}
+ <div{{ description_attributes }}>{{ description }}</div>
+ {%- endif -%}
+
{{ children }}
{{ value }}
</details>
diff --git a/core/modules/system/templates/field-multiple-value-form.html.twig b/core/modules/system/templates/field-multiple-value-form.html.twig
index 832b9f61794..ecd268690b4 100644
--- a/core/modules/system/templates/field-multiple-value-form.html.twig
+++ b/core/modules/system/templates/field-multiple-value-form.html.twig
@@ -16,7 +16,7 @@
* - attributes: HTML attributes to apply to the description container.
* - button: "Add another item" button.
*
- * @see template_preprocess_field_multiple_value_form()
+ * @see \Drupal\Core\Field\FieldPreprocess::preprocessFieldMultipleValueForm()
*
* @ingroup themeable
*/
diff --git a/core/modules/system/templates/field.html.twig b/core/modules/system/templates/field.html.twig
index 1497678b50a..2bef0a02e6f 100644
--- a/core/modules/system/templates/field.html.twig
+++ b/core/modules/system/templates/field.html.twig
@@ -33,7 +33,7 @@
* - field_type: The type of the field.
* - label_display: The display settings for the label.
*
- * @see template_preprocess_field()
+ * @see \Drupal\Core\Field\FieldPreprocess::preprocessField()
*
* @ingroup themeable
*/
diff --git a/core/modules/system/templates/image.html.twig b/core/modules/system/templates/image.html.twig
index 6411eaa3d07..1f6d19d6c3e 100644
--- a/core/modules/system/templates/image.html.twig
+++ b/core/modules/system/templates/image.html.twig
@@ -7,7 +7,7 @@
* - attributes: HTML attributes for the img tag.
* - style_name: (optional) The name of the image style applied.
*
- * @see template_preprocess_image()
+ * @see \Drupal\Core\Theme\ImagePreprocess::preprocessImage()
*
* @ingroup themeable
*/
diff --git a/core/modules/system/templates/install-page.html.twig b/core/modules/system/templates/install-page.html.twig
index f6091fd3b95..d9144e6a154 100644
--- a/core/modules/system/templates/install-page.html.twig
+++ b/core/modules/system/templates/install-page.html.twig
@@ -6,7 +6,7 @@
* All available variables are mirrored in page.html.twig.
* Some may be blank but they are provided for consistency.
*
- * @see template_preprocess_install_page()
+ * @see \Drupal\Core\Theme\ThemePreprocess::preprocessInstallPage()
*
* @ingroup themeable
*/
diff --git a/core/modules/system/templates/item-list.html.twig b/core/modules/system/templates/item-list.html.twig
index 1462cf41ae0..c2babdab978 100644
--- a/core/modules/system/templates/item-list.html.twig
+++ b/core/modules/system/templates/item-list.html.twig
@@ -16,7 +16,7 @@
* - context: A list of contextual data associated with the list. May contain:
* - list_style: The custom list style.
*
- * @see template_preprocess_item_list()
+ * @see \Drupal\Core\Theme\ThemePreprocess::preprocessItemList()
*
* @ingroup themeable
*/
diff --git a/core/modules/system/templates/maintenance-page.html.twig b/core/modules/system/templates/maintenance-page.html.twig
index 748ed5a3aa4..06fb6065f7a 100644
--- a/core/modules/system/templates/maintenance-page.html.twig
+++ b/core/modules/system/templates/maintenance-page.html.twig
@@ -6,7 +6,7 @@
* All available variables are mirrored in page.html.twig.
* Some may be blank but they are provided for consistency.
*
- * @see template_preprocess_maintenance_page()
+ * @see \Drupal\Core\Theme\ThemePreprocess::preprocessMaintenancePage()
*
* @ingroup themeable
*/
diff --git a/core/modules/system/templates/menu-local-action.html.twig b/core/modules/system/templates/menu-local-action.html.twig
index 0eb03a9534a..e0280d5fcbc 100644
--- a/core/modules/system/templates/menu-local-action.html.twig
+++ b/core/modules/system/templates/menu-local-action.html.twig
@@ -7,7 +7,7 @@
* - attributes: HTML attributes for the wrapper element.
* - link: A rendered link element.
*
- * @see template_preprocess_menu_local_action()
+ * @see \Drupal\Core\Menu\MenuPreprocess::preprocessMenuLocalAction()
*
* @ingroup themeable
*/
diff --git a/core/modules/system/templates/menu-local-task.html.twig b/core/modules/system/templates/menu-local-task.html.twig
index ec02a8d530c..b2a743940a7 100644
--- a/core/modules/system/templates/menu-local-task.html.twig
+++ b/core/modules/system/templates/menu-local-task.html.twig
@@ -11,7 +11,7 @@
* Note: This template renders the content for each task item in
* menu-local-tasks.html.twig.
*
- * @see template_preprocess_menu_local_task()
+ * @see \Drupal\Core\Menu\MenuPreprocess::preprocessMenuLocalTask()
*
* @ingroup themeable
*/
diff --git a/core/modules/system/templates/pager.html.twig b/core/modules/system/templates/pager.html.twig
index 199f0578dbd..75047c1b95f 100644
--- a/core/modules/system/templates/pager.html.twig
+++ b/core/modules/system/templates/pager.html.twig
@@ -28,7 +28,7 @@
* at the first page.
* - next: Present if the visible list of pages ends before the last page.
*
- * @see template_preprocess_pager()
+ * @see \Drupal\Core\Pager\PagerPreprocess::preprocessPager()
*
* @ingroup themeable
*/
diff --git a/core/modules/system/templates/region.html.twig b/core/modules/system/templates/region.html.twig
index 219e14b0a4b..ddcaaa192df 100644
--- a/core/modules/system/templates/region.html.twig
+++ b/core/modules/system/templates/region.html.twig
@@ -9,7 +9,7 @@
* - region: The name of the region variable as defined in the theme's
* .info.yml file.
*
- * @see template_preprocess_region()
+ * @see \Drupal\Core\Theme\ThemePreprocess::preprocessRegion()
*
* @ingroup themeable
*/
diff --git a/core/modules/system/templates/table.html.twig b/core/modules/system/templates/table.html.twig
index cfcb0bf976c..6a73cc1152a 100644
--- a/core/modules/system/templates/table.html.twig
+++ b/core/modules/system/templates/table.html.twig
@@ -38,7 +38,7 @@
* - no_striping: A boolean indicating that the row should receive no striping.
* - header_columns: The number of columns in the header.
*
- * @see template_preprocess_table()
+ * @see \Drupal\Core\Theme\ThemePreprocess::preprocessTable()
*
* @ingroup themeable
*/
diff --git a/core/modules/system/tests/fixtures/update/drupal-10.3.0.bare.standard.php.gz b/core/modules/system/tests/fixtures/update/drupal-10.3.0.bare.standard.php.gz
index 5d8c9974469..077d0645ddc 100644
--- a/core/modules/system/tests/fixtures/update/drupal-10.3.0.bare.standard.php.gz
+++ b/core/modules/system/tests/fixtures/update/drupal-10.3.0.bare.standard.php.gz
Binary files differ
diff --git a/core/modules/system/tests/fixtures/update/drupal-10.3.0.filled.standard.php.gz b/core/modules/system/tests/fixtures/update/drupal-10.3.0.filled.standard.php.gz
index 423f49a1d40..5db0b3a5aae 100644
--- a/core/modules/system/tests/fixtures/update/drupal-10.3.0.filled.standard.php.gz
+++ b/core/modules/system/tests/fixtures/update/drupal-10.3.0.filled.standard.php.gz
Binary files differ
diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml b/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml
index f9ca25544ab..95b7a1e4c0f 100644
--- a/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml
+++ b/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml
@@ -43,6 +43,13 @@ ajax_test.insert_links_inline_wrapper:
requirements:
_access: 'TRUE'
+ajax_test.insert_links_table_wrapper:
+ path: '/ajax-test/insert-table-wrapper'
+ defaults:
+ _controller: '\Drupal\ajax_test\Controller\AjaxTestController::insertLinksTableWrapper'
+ requirements:
+ _access: 'TRUE'
+
ajax_test.dialog_close:
path: '/ajax-test/dialog-close'
defaults:
diff --git a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
index aea5322ce2d..b0adea8e7c7 100644
--- a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
+++ b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
@@ -141,6 +141,36 @@ class AjaxTestController {
}
/**
+ * Returns a render array of links that directly Drupal.ajax().
+ *
+ * @return array
+ * Renderable array of AJAX response contents.
+ */
+ public function insertLinksTableWrapper(): array {
+ $build['links'] = [
+ 'ajax_target' => [
+ '#markup' => '<div class="ajax-target-wrapper"><table><tbody id="ajax-target"></tbody></table></div>',
+ ],
+ 'links' => [
+ '#theme' => 'links',
+ '#attached' => ['library' => ['ajax_test/ajax_insert']],
+ ],
+ ];
+
+ $build['links']['links']['#links']['table-row'] = [
+ 'title' => 'Link table-row',
+ 'url' => Url::fromRoute('ajax_test.ajax_render_types', ['type' => 'table-row']),
+ 'attributes' => [
+ 'class' => ['ajax-insert'],
+ 'data-method' => 'html',
+ 'data-effect' => 'none',
+ ],
+ ];
+
+ return $build;
+ }
+
+ /**
* Returns a render array that will be rendered by AjaxRenderer.
*
* Verifies that the response incorporates JavaScript settings generated
@@ -336,6 +366,7 @@ class AjaxTestController {
'comment-not-wrapped' => '<!-- COMMENT --><div class="comment-not-wrapped">comment-not-wrapped</div>',
'svg' => '<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10"><rect x="0" y="0" height="10" width="10" fill="green"></rect></svg>',
'empty' => '',
+ 'table-row' => '<tr><td>table-row</td></tr>',
];
$render_multiple_root = [
'mixed' => ' foo <!-- COMMENT --> foo bar<div class="a class"><p>some string</p></div> additional not wrapped strings, <!-- ANOTHER COMMENT --> <p>final string</p>',
diff --git a/core/modules/system/tests/modules/batch_test/src/BatchTestCallbacks.php b/core/modules/system/tests/modules/batch_test/src/BatchTestCallbacks.php
index 24da9ebff84..aa4dc2e1a29 100644
--- a/core/modules/system/tests/modules/batch_test/src/BatchTestCallbacks.php
+++ b/core/modules/system/tests/modules/batch_test/src/BatchTestCallbacks.php
@@ -111,7 +111,7 @@ class BatchTestCallbacks {
// 'finished' callback.
$batch_test_helper->stack("op 5 id $id");
$context['results'][5][] = $id;
- // This test is to test finished > 1
+ // This test is to test finished > 1.
$context['finished'] = 3.14;
}
diff --git a/core/modules/system/tests/modules/common_test/common_test.module b/core/modules/system/tests/modules/common_test/common_test.module
index 5d7fdc3dc6b..5a17e7a6fcd 100644
--- a/core/modules/system/tests/modules/common_test/common_test.module
+++ b/core/modules/system/tests/modules/common_test/common_test.module
@@ -10,8 +10,9 @@ declare(strict_types=1);
/**
* Implements hook_TYPE_alter().
*
- * Same as common_test_drupal_alter_alter(), but here, we verify that themes
- * can also alter and come last.
+ * Same as CommonTestHooks::drupalAlterAlter(), but here, we verify that themes
+ * can also alter and come last. This file gets included by
+ * CommonTestHooks::includeThemeFunction().
*/
function olivero_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL): void {
// Alter first argument.
@@ -40,27 +41,3 @@ function olivero_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL): void
}
}
}
-
-/**
- * Implements MODULE_preprocess().
- *
- * @see RenderTest::testDrupalRenderThemePreprocessAttached()
- */
-function common_test_preprocess(&$variables, $hook): void {
- if (!\Drupal::state()->get('theme_preprocess_attached_test', FALSE)) {
- return;
- }
- $variables['#attached']['library'][] = 'test/generic_preprocess';
-}
-
-/**
- * Implements MODULE_preprocess_HOOK().
- *
- * @see RenderTest::testDrupalRenderThemePreprocessAttached()
- */
-function common_test_preprocess_common_test_render_element(&$variables): void {
- if (!\Drupal::state()->get('theme_preprocess_attached_test', FALSE)) {
- return;
- }
- $variables['#attached']['library'][] = 'test/specific_preprocess';
-}
diff --git a/core/modules/system/tests/modules/common_test/src/Hook/CommonTestHooks.php b/core/modules/system/tests/modules/common_test/src/Hook/CommonTestHooks.php
index a3e65453b04..aa93bfb5083 100644
--- a/core/modules/system/tests/modules/common_test/src/Hook/CommonTestHooks.php
+++ b/core/modules/system/tests/modules/common_test/src/Hook/CommonTestHooks.php
@@ -4,8 +4,6 @@ declare(strict_types=1);
namespace Drupal\common_test\Hook;
-use Drupal\Core\Language\LanguageInterface;
-use Drupal\Core\Asset\AttachedAssetsInterface;
use Drupal\Core\Hook\Attribute\Hook;
/**
@@ -59,53 +57,6 @@ class CommonTestHooks {
}
/**
- * Implements hook_theme().
- */
- #[Hook('theme')]
- public function theme() : array {
- return [
- 'common_test_foo' => [
- 'variables' => [
- 'foo' => 'foo',
- 'bar' => 'bar',
- ],
- ],
- 'common_test_render_element' => [
- 'render element' => 'foo',
- ],
- ];
- }
-
- /**
- * Implements hook_library_info_build().
- */
- #[Hook('library_info_build')]
- public function libraryInfoBuild(): array {
- $libraries = [];
- if (\Drupal::state()->get('common_test.library_info_build_test')) {
- $libraries['dynamic_library'] = ['version' => '1.0', 'css' => ['base' => ['common_test.css' => []]]];
- }
- return $libraries;
- }
-
- /**
- * Implements hook_library_info_alter().
- */
- #[Hook('library_info_alter')]
- public function libraryInfoAlter(&$libraries, $module): void {
- if ($module === 'core' && isset($libraries['loadjs'])) {
- // Change the version of loadjs to 0.0.
- $libraries['loadjs']['version'] = '0.0';
- // Make loadjs depend on jQuery Form to test library dependencies.
- $libraries['loadjs']['dependencies'][] = 'core/internal.jquery.form';
- }
- // Alter the dynamically registered library definition.
- if ($module === 'common_test' && isset($libraries['dynamic_library'])) {
- $libraries['dynamic_library']['dependencies'] = ['core/jquery'];
- }
- }
-
- /**
* Implements hook_cron().
*
* System module should handle if a module does not catch an exception and
@@ -118,80 +69,4 @@ class CommonTestHooks {
throw new \Exception('Uncaught exception');
}
- /**
- * Implements hook_page_attachments().
- *
- * @see \Drupal\system\Tests\Common\PageRenderTest::assertPageRenderHookExceptions()
- */
- #[Hook('page_attachments')]
- public function pageAttachments(array &$page): void {
- $page['#attached']['library'][] = 'core/foo';
- $page['#attached']['library'][] = 'core/bar';
- $page['#cache']['tags'] = ['example'];
- $page['#cache']['contexts'] = ['user.permissions'];
- if (\Drupal::state()->get('common_test.hook_page_attachments.descendant_attached', FALSE)) {
- $page['content']['#attached']['library'][] = 'core/jquery';
- }
- if (\Drupal::state()->get('common_test.hook_page_attachments.render_array', FALSE)) {
- $page['something'] = ['#markup' => 'test'];
- }
- if (\Drupal::state()->get('common_test.hook_page_attachments.early_rendering', FALSE)) {
- // Do some early rendering.
- $element = ['#markup' => '123'];
- \Drupal::service('renderer')->render($element);
- }
- }
-
- /**
- * Implements hook_page_attachments_alter().
- *
- * @see \Drupal\system\Tests\Common\PageRenderTest::assertPageRenderHookExceptions()
- */
- #[Hook('page_attachments_alter')]
- public function pageAttachmentsAlter(array &$page): void {
- // Remove a library that was added in common_test_page_attachments(), to
- // test that this hook can do what it claims to do.
- if (isset($page['#attached']['library']) && ($index = array_search('core/bar', $page['#attached']['library'])) && $index !== FALSE) {
- unset($page['#attached']['library'][$index]);
- }
- $page['#attached']['library'][] = 'core/baz';
- $page['#cache']['tags'] = ['example'];
- $page['#cache']['contexts'] = ['user.permissions'];
- if (\Drupal::state()->get('common_test.hook_page_attachments_alter.descendant_attached', FALSE)) {
- $page['content']['#attached']['library'][] = 'core/jquery';
- }
- if (\Drupal::state()->get('common_test.hook_page_attachments_alter.render_array', FALSE)) {
- $page['something'] = ['#markup' => 'test'];
- }
- }
-
- /**
- * Implements hook_js_alter().
- *
- * @see \Drupal\KernelTests\Core\Asset\AttachedAssetsTest::testAlter()
- */
- #[Hook('js_alter')]
- public function jsAlter(&$javascript, AttachedAssetsInterface $assets, LanguageInterface $language): void {
- // Attach alter.js above tableselect.js.
- $alter_js = \Drupal::service('extension.list.module')->getPath('common_test') . '/alter.js';
- if (array_key_exists($alter_js, $javascript) && array_key_exists('core/misc/tableselect.js', $javascript)) {
- $javascript[$alter_js]['weight'] = $javascript['core/misc/tableselect.js']['weight'] - 1;
- }
- }
-
- /**
- * Implements hook_js_settings_alter().
- *
- * @see \Drupal\system\Tests\Common\JavaScriptTest::testHeaderSetting()
- */
- #[Hook('js_settings_alter')]
- public function jsSettingsAlter(&$settings, AttachedAssetsInterface $assets): void {
- // Modify an existing setting.
- if (array_key_exists('pluralDelimiter', $settings)) {
- $settings['pluralDelimiter'] = '☃';
- }
- // Add a setting.
- $settings['foo'] = 'bar';
- }
-
}
diff --git a/core/modules/system/tests/modules/common_test/src/Hook/CommonTestThemeHooks.php b/core/modules/system/tests/modules/common_test/src/Hook/CommonTestThemeHooks.php
new file mode 100644
index 00000000000..f47116e8920
--- /dev/null
+++ b/core/modules/system/tests/modules/common_test/src/Hook/CommonTestThemeHooks.php
@@ -0,0 +1,165 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\common_test\Hook;
+
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Asset\AttachedAssetsInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for common_test.
+ */
+class CommonTestThemeHooks {
+
+ /**
+ * Implements hook_theme().
+ */
+ #[Hook('theme')]
+ public function theme() : array {
+ return [
+ 'common_test_foo' => [
+ 'variables' => [
+ 'foo' => 'foo',
+ 'bar' => 'bar',
+ ],
+ ],
+ 'common_test_render_element' => [
+ 'render element' => 'foo',
+ ],
+ ];
+ }
+
+ /**
+ * Implements hook_library_info_build().
+ */
+ #[Hook('library_info_build')]
+ public function libraryInfoBuild(): array {
+ $libraries = [];
+ if (\Drupal::state()->get('common_test.library_info_build_test')) {
+ $libraries['dynamic_library'] = ['version' => '1.0', 'css' => ['base' => ['common_test.css' => []]]];
+ }
+ return $libraries;
+ }
+
+ /**
+ * Implements hook_library_info_alter().
+ */
+ #[Hook('library_info_alter')]
+ public function libraryInfoAlter(&$libraries, $module): void {
+ if ($module === 'core' && isset($libraries['loadjs'])) {
+ // Change the version of loadjs to 0.0.
+ $libraries['loadjs']['version'] = '0.0';
+ // Make loadjs depend on jQuery Form to test library dependencies.
+ $libraries['loadjs']['dependencies'][] = 'core/internal.jquery.form';
+ }
+ // Alter the dynamically registered library definition.
+ if ($module === 'common_test' && isset($libraries['dynamic_library'])) {
+ $libraries['dynamic_library']['dependencies'] = ['core/jquery'];
+ }
+ }
+
+ /**
+ * Implements hook_page_attachments().
+ *
+ * @see \Drupal\system\Tests\Common\PageRenderTest::assertPageRenderHookExceptions()
+ */
+ #[Hook('page_attachments')]
+ public function pageAttachments(array &$page): void {
+ $page['#attached']['library'][] = 'core/foo';
+ $page['#attached']['library'][] = 'core/bar';
+ $page['#cache']['tags'] = ['example'];
+ $page['#cache']['contexts'] = ['user.permissions'];
+ if (\Drupal::state()->get('common_test.hook_page_attachments.descendant_attached', FALSE)) {
+ $page['content']['#attached']['library'][] = 'core/jquery';
+ }
+ if (\Drupal::state()->get('common_test.hook_page_attachments.render_array', FALSE)) {
+ $page['something'] = ['#markup' => 'test'];
+ }
+ if (\Drupal::state()->get('common_test.hook_page_attachments.early_rendering', FALSE)) {
+ // Do some early rendering.
+ $element = ['#markup' => '123'];
+ \Drupal::service('renderer')->render($element);
+ }
+ }
+
+ /**
+ * Implements hook_page_attachments_alter().
+ *
+ * @see \Drupal\system\Tests\Common\PageRenderTest::assertPageRenderHookExceptions()
+ */
+ #[Hook('page_attachments_alter')]
+ public function pageAttachmentsAlter(array &$page): void {
+ // Remove a library that was added in common_test_page_attachments(), to
+ // test that this hook can do what it claims to do.
+ if (isset($page['#attached']['library']) && ($index = array_search('core/bar', $page['#attached']['library'])) && $index !== FALSE) {
+ unset($page['#attached']['library'][$index]);
+ }
+ $page['#attached']['library'][] = 'core/baz';
+ $page['#cache']['tags'] = ['example'];
+ $page['#cache']['contexts'] = ['user.permissions'];
+ if (\Drupal::state()->get('common_test.hook_page_attachments_alter.descendant_attached', FALSE)) {
+ $page['content']['#attached']['library'][] = 'core/jquery';
+ }
+ if (\Drupal::state()->get('common_test.hook_page_attachments_alter.render_array', FALSE)) {
+ $page['something'] = ['#markup' => 'test'];
+ }
+ }
+
+ /**
+ * Implements hook_js_alter().
+ *
+ * @see \Drupal\KernelTests\Core\Asset\AttachedAssetsTest::testAlter()
+ */
+ #[Hook('js_alter')]
+ public function jsAlter(&$javascript, AttachedAssetsInterface $assets, LanguageInterface $language): void {
+ // Attach alter.js above tableselect.js.
+ $alter_js = \Drupal::service('extension.list.module')->getPath('common_test') . '/alter.js';
+ if (array_key_exists($alter_js, $javascript) && array_key_exists('core/misc/tableselect.js', $javascript)) {
+ $javascript[$alter_js]['weight'] = $javascript['core/misc/tableselect.js']['weight'] - 1;
+ }
+ }
+
+ /**
+ * Implements hook_js_settings_alter().
+ *
+ * @see \Drupal\system\Tests\Common\JavaScriptTest::testHeaderSetting()
+ */
+ #[Hook('js_settings_alter')]
+ public function jsSettingsAlter(&$settings, AttachedAssetsInterface $assets): void {
+ // Modify an existing setting.
+ if (array_key_exists('pluralDelimiter', $settings)) {
+ $settings['pluralDelimiter'] = '☃';
+ }
+ // Add a setting.
+ $settings['foo'] = 'bar';
+ }
+
+ /**
+ * Implements hook_preprocess().
+ *
+ * @see RenderTest::testDrupalRenderThemePreprocessAttached()
+ */
+ #[Hook('preprocess')]
+ public function preprocess(&$variables, $hook): void {
+ if (!\Drupal::state()->get('theme_preprocess_attached_test', FALSE)) {
+ return;
+ }
+ $variables['#attached']['library'][] = 'test/generic_preprocess';
+ }
+
+ /**
+ * Implements hook_preprocess_HOOK().
+ *
+ * @see RenderTest::testDrupalRenderThemePreprocessAttached()
+ */
+ #[Hook('preprocess_common_test_render_element')]
+ public function commonTestRenderElement(&$variables): void {
+ if (!\Drupal::state()->get('theme_preprocess_attached_test', FALSE)) {
+ return;
+ }
+ $variables['#attached']['library'][] = 'test/specific_preprocess';
+ }
+
+}
diff --git a/core/modules/system/tests/modules/container_initialize/container_initialize.info.yml b/core/modules/system/tests/modules/container_initialize/container_initialize.info.yml
new file mode 100644
index 00000000000..46411d2ea54
--- /dev/null
+++ b/core/modules/system/tests/modules/container_initialize/container_initialize.info.yml
@@ -0,0 +1,5 @@
+name: 'Container initialize'
+type: module
+description: 'Support module for HookCollectorPass testing.'
+package: Testing
+version: VERSION
diff --git a/core/modules/system/tests/modules/container_initialize/container_initialize.module b/core/modules/system/tests/modules/container_initialize/container_initialize.module
new file mode 100644
index 00000000000..5c8e0aff74e
--- /dev/null
+++ b/core/modules/system/tests/modules/container_initialize/container_initialize.module
@@ -0,0 +1,10 @@
+<?php
+
+/**
+ * @file
+ * Used to test bare container calls in .module files.
+ */
+
+declare(strict_types=1);
+
+\Drupal::getContainer()->getParameter('site.path');
diff --git a/core/modules/system/tests/modules/delay_cache_tags_invalidation/src/Hook/DelayCacheTagsInvalidationHooks.php b/core/modules/system/tests/modules/delay_cache_tags_invalidation/src/Hook/DelayCacheTagsInvalidationHooks.php
index c543a1d3e53..f444b69a427 100644
--- a/core/modules/system/tests/modules/delay_cache_tags_invalidation/src/Hook/DelayCacheTagsInvalidationHooks.php
+++ b/core/modules/system/tests/modules/delay_cache_tags_invalidation/src/Hook/DelayCacheTagsInvalidationHooks.php
@@ -25,11 +25,11 @@ class DelayCacheTagsInvalidationHooks {
}
// Read the pre-transaction cache writes.
// @see \Drupal\KernelTests\Core\Cache\EndOfTransactionQueriesTest::testEntitySave()
- \Drupal::state()->set('delay_cache_tags_invalidation_entity_test_insert' . '__pre-transaction_foobar', \Drupal::cache()->get('test_cache_pre-transaction_foobar'));
- \Drupal::state()->set('delay_cache_tags_invalidation_entity_test_insert' . '__pre-transaction_entity_test_list', \Drupal::cache()->get('test_cache_pre-transaction_entity_test_list'));
+ \Drupal::state()->set('delay_cache_tags_invalidation_entity_test_insert__pre-transaction_foobar', \Drupal::cache()->get('test_cache_pre-transaction_foobar'));
+ \Drupal::state()->set('delay_cache_tags_invalidation_entity_test_insert__pre-transaction_entity_test_list', \Drupal::cache()->get('test_cache_pre-transaction_entity_test_list'));
// Write during the transaction.
- \Drupal::cache()->set('delay_cache_tags_invalidation_entity_test_insert' . '__during_transaction_foobar', 'something', Cache::PERMANENT, ['foobar']);
- \Drupal::cache()->set('delay_cache_tags_invalidation_entity_test_insert' . '__during_transaction_entity_test_list', 'something', Cache::PERMANENT, ['entity_test_list']);
+ \Drupal::cache()->set('delay_cache_tags_invalidation_entity_test_insert__during_transaction_foobar', 'something', Cache::PERMANENT, ['foobar']);
+ \Drupal::cache()->set('delay_cache_tags_invalidation_entity_test_insert__during_transaction_entity_test_list', 'something', Cache::PERMANENT, ['entity_test_list']);
// Trigger a nested entity save and hence a nested transaction.
User::create(['name' => 'john doe', 'status' => 1])->save();
}
@@ -42,8 +42,8 @@ class DelayCacheTagsInvalidationHooks {
if ($entity->getAccountName() === 'john doe') {
// Read the in-transaction cache writes.
// @see delay_cache_tags_invalidation_entity_test_insert()
- \Drupal::state()->set('delay_cache_tags_invalidation_user_insert' . '__during_transaction_foobar', \Drupal::cache()->get('delay_cache_tags_invalidation_entity_test_insert__during_transaction_foobar'));
- \Drupal::state()->set('delay_cache_tags_invalidation_user_insert' . '__during_transaction_entity_test_list', \Drupal::cache()->get('delay_cache_tags_invalidation_entity_test_insert__during_transaction_entity_test_list'));
+ \Drupal::state()->set('delay_cache_tags_invalidation_user_insert__during_transaction_foobar', \Drupal::cache()->get('delay_cache_tags_invalidation_entity_test_insert__during_transaction_foobar'));
+ \Drupal::state()->set('delay_cache_tags_invalidation_user_insert__during_transaction_entity_test_list', \Drupal::cache()->get('delay_cache_tags_invalidation_entity_test_insert__during_transaction_entity_test_list'));
}
}
diff --git a/core/modules/system/tests/modules/element_info_test/src/Element/Deprecated.php b/core/modules/system/tests/modules/element_info_test/src/Element/Deprecated.php
index f54ad8f9a9a..c052c5a488f 100644
--- a/core/modules/system/tests/modules/element_info_test/src/Element/Deprecated.php
+++ b/core/modules/system/tests/modules/element_info_test/src/Element/Deprecated.php
@@ -17,7 +17,8 @@ class Deprecated extends RenderElementBase {
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
- parent::__construct($configuration, $plugin_id, $plugin_definition);
+ $elementInfoManager = \Drupal::service('plugin.manager.element_info');
+ parent::__construct($configuration, $plugin_id, $plugin_definition, $elementInfoManager);
@trigger_error(__CLASS__ . ' is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. See https://www.drupal.org/node/3068104', E_USER_DEPRECATED);
}
diff --git a/core/modules/system/tests/modules/element_info_test/src/Hook/ElementInfoTestHooks.php b/core/modules/system/tests/modules/element_info_test/src/Hook/ElementInfoTestHooks.php
index ea72bd033fb..53150495cb9 100644
--- a/core/modules/system/tests/modules/element_info_test/src/Hook/ElementInfoTestHooks.php
+++ b/core/modules/system/tests/modules/element_info_test/src/Hook/ElementInfoTestHooks.php
@@ -4,8 +4,9 @@ declare(strict_types=1);
namespace Drupal\element_info_test\Hook;
-use Drupal\element_info_test\ElementInfoTestNumberBuilder;
use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\element_info_test\ElementInfoTestNumberBuilder;
+use Drupal\element_info_test\Render\Element\Details;
/**
* Hook implementations for element_info_test.
@@ -30,6 +31,9 @@ class ElementInfoTestHooks {
if (\Drupal::state()->get('hook_element_plugin_alter:remove_weight', FALSE)) {
unset($definitions['weight']);
}
+
+ $definitions['details']['class'] = Details::class;
+ $definitions['details']['provider'] = 'element_info_test';
}
}
diff --git a/core/modules/system/tests/modules/element_info_test/src/Render/Element/Details.php b/core/modules/system/tests/modules/element_info_test/src/Render/Element/Details.php
new file mode 100644
index 00000000000..8e4d84558ca
--- /dev/null
+++ b/core/modules/system/tests/modules/element_info_test/src/Render/Element/Details.php
@@ -0,0 +1,57 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\element_info_test\Render\Element;
+
+use Drupal\Core\Render\Attribute\RenderElement;
+use Drupal\Core\Render\Element;
+use Drupal\Core\Render\Element\RenderElementBase;
+
+/**
+ * Provides a render element for a details element.
+ *
+ * Properties:
+ *
+ * @property $title
+ * The title of the details container. Defaults to "Details".
+ * @property $open
+ * Indicates whether the container should be open by default.
+ * Defaults to FALSE.
+ * @property $custom
+ * Confirm that this class has been swapped properly.
+ * @property $summary_attributes
+ * An array of attributes to apply to the <summary>
+ * element.
+ */
+#[RenderElement('details')]
+class Details extends RenderElementBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getInfo(): array {
+ return [
+ '#open' => FALSE,
+ '#summary_attributes' => [],
+ '#custom' => 'Custom',
+ ];
+ }
+
+ /**
+ * Adds form element theming to details.
+ *
+ * @param array $element
+ * An associative array containing the properties and children of the
+ * details.
+ *
+ * @return array
+ * The modified element.
+ */
+ public static function preRenderDetails($element): array {
+ Element::setAttributes($element, ['custom']);
+
+ return $element;
+ }
+
+}
diff --git a/core/modules/system/tests/modules/entity_crud_hook_test/src/Hook/EntityCrudHookTestHooks.php b/core/modules/system/tests/modules/entity_crud_hook_test/src/Hook/EntityCrudHookTestHooks.php
index c552a268952..8e16b55b4c6 100644
--- a/core/modules/system/tests/modules/entity_crud_hook_test/src/Hook/EntityCrudHookTestHooks.php
+++ b/core/modules/system/tests/modules/entity_crud_hook_test/src/Hook/EntityCrudHookTestHooks.php
@@ -17,7 +17,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('entity_create')]
public function entityCreate(EntityInterface $entity): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_create' . ' called for type ' . $entity->getEntityTypeId();
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_create called for type ' . $entity->getEntityTypeId();
}
/**
@@ -25,7 +25,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('block_create')]
public function blockCreate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_create' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_create called';
}
/**
@@ -33,7 +33,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('comment_create')]
public function commentCreate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_create' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_create called';
}
/**
@@ -41,7 +41,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('file_create')]
public function fileCreate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_create' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_create called';
}
/**
@@ -49,7 +49,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('node_create')]
public function nodeCreate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_create' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_create called';
}
/**
@@ -57,7 +57,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_term_create')]
public function taxonomyTermCreate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_create' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_create called';
}
/**
@@ -65,7 +65,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_vocabulary_create')]
public function taxonomyVocabularyCreate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_create' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_create called';
}
/**
@@ -73,7 +73,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('user_create')]
public function userCreate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_create' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_create called';
}
/**
@@ -81,7 +81,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('entity_presave')]
public function entityPresave(EntityInterface $entity): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_presave' . ' called for type ' . $entity->getEntityTypeId();
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_presave called for type ' . $entity->getEntityTypeId();
}
/**
@@ -89,7 +89,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('block_presave')]
public function blockPresave(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_presave' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_presave called';
}
/**
@@ -97,7 +97,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('comment_presave')]
public function commentPresave(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_presave' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_presave called';
}
/**
@@ -105,7 +105,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('file_presave')]
public function filePresave(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_presave' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_presave called';
}
/**
@@ -113,7 +113,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('node_presave')]
public function nodePresave(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_presave' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_presave called';
}
/**
@@ -121,7 +121,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_term_presave')]
public function taxonomyTermPresave(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_presave' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_presave called';
}
/**
@@ -129,7 +129,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_vocabulary_presave')]
public function taxonomyVocabularyPresave(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_presave' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_presave called';
}
/**
@@ -137,7 +137,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('user_presave')]
public function userPresave(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_presave' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_presave called';
}
/**
@@ -145,7 +145,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('entity_insert')]
public function entityInsert(EntityInterface $entity): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_insert' . ' called for type ' . $entity->getEntityTypeId();
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_insert called for type ' . $entity->getEntityTypeId();
}
/**
@@ -153,7 +153,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('block_insert')]
public function blockInsert(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_insert' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_insert called';
}
/**
@@ -161,7 +161,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('comment_insert')]
public function commentInsert(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_insert' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_insert called';
}
/**
@@ -169,7 +169,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('file_insert')]
public function fileInsert(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_insert' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_insert called';
}
/**
@@ -177,7 +177,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('node_insert')]
public function nodeInsert(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_insert' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_insert called';
}
/**
@@ -185,7 +185,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_term_insert')]
public function taxonomyTermInsert(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_insert' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_insert called';
}
/**
@@ -193,7 +193,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_vocabulary_insert')]
public function taxonomyVocabularyInsert(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_insert' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_insert called';
}
/**
@@ -201,7 +201,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('user_insert')]
public function userInsert(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_insert' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_insert called';
}
/**
@@ -209,7 +209,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('entity_preload')]
public function entityPreload(array $entities, $type): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_preload' . ' called for type ' . $type;
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_preload called for type ' . $type;
}
/**
@@ -217,7 +217,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('entity_load')]
public function entityLoad(array $entities, $type): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_load' . ' called for type ' . $type;
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_load called for type ' . $type;
}
/**
@@ -225,7 +225,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('block_load')]
public function blockLoad(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_load' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_load called';
}
/**
@@ -233,7 +233,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('comment_load')]
public function commentLoad(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_load' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_load called';
}
/**
@@ -241,7 +241,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('file_load')]
public function fileLoad(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_load' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_load called';
}
/**
@@ -249,7 +249,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('node_load')]
public function nodeLoad(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_load' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_load called';
}
/**
@@ -257,7 +257,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_term_load')]
public function taxonomyTermLoad(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_load' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_load called';
}
/**
@@ -265,7 +265,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_vocabulary_load')]
public function taxonomyVocabularyLoad(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_load' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_load called';
}
/**
@@ -273,7 +273,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('user_load')]
public function userLoad(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_load' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_load called';
}
/**
@@ -281,7 +281,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('entity_update')]
public function entityUpdate(EntityInterface $entity): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_update' . ' called for type ' . $entity->getEntityTypeId();
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_update called for type ' . $entity->getEntityTypeId();
}
/**
@@ -289,7 +289,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('block_update')]
public function blockUpdate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_update' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_update called';
}
/**
@@ -297,7 +297,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('comment_update')]
public function commentUpdate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_update' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_update called';
}
/**
@@ -305,7 +305,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('file_update')]
public function fileUpdate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_update' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_update called';
}
/**
@@ -313,7 +313,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('node_update')]
public function nodeUpdate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_update' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_update called';
}
/**
@@ -321,7 +321,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_term_update')]
public function taxonomyTermUpdate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_update' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_update called';
}
/**
@@ -329,7 +329,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_vocabulary_update')]
public function taxonomyVocabularyUpdate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_update' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_update called';
}
/**
@@ -337,7 +337,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('user_update')]
public function userUpdate(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_update' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_update called';
}
/**
@@ -345,7 +345,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('entity_predelete')]
public function entityPredelete(EntityInterface $entity): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_predelete' . ' called for type ' . $entity->getEntityTypeId();
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_predelete called for type ' . $entity->getEntityTypeId();
}
/**
@@ -353,7 +353,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('block_predelete')]
public function blockPredelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_predelete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_predelete called';
}
/**
@@ -361,7 +361,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('comment_predelete')]
public function commentPredelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_predelete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_predelete called';
}
/**
@@ -369,7 +369,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('file_predelete')]
public function filePredelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_predelete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_predelete called';
}
/**
@@ -377,7 +377,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('node_predelete')]
public function nodePredelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_predelete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_predelete called';
}
/**
@@ -385,7 +385,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_term_predelete')]
public function taxonomyTermPredelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_predelete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_predelete called';
}
/**
@@ -393,7 +393,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_vocabulary_predelete')]
public function taxonomyVocabularyPredelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_predelete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_predelete called';
}
/**
@@ -401,7 +401,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('user_predelete')]
public function userPredelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_predelete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_predelete called';
}
/**
@@ -409,7 +409,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('entity_delete')]
public function entityDelete(EntityInterface $entity): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_delete' . ' called for type ' . $entity->getEntityTypeId();
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_entity_delete called for type ' . $entity->getEntityTypeId();
}
/**
@@ -417,7 +417,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('block_delete')]
public function blockDelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_delete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_block_delete called';
}
/**
@@ -425,7 +425,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('comment_delete')]
public function commentDelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_delete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_comment_delete called';
}
/**
@@ -433,7 +433,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('file_delete')]
public function fileDelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_delete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_file_delete called';
}
/**
@@ -441,7 +441,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('node_delete')]
public function nodeDelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_delete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_node_delete called';
}
/**
@@ -449,7 +449,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_term_delete')]
public function taxonomyTermDelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_delete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_term_delete called';
}
/**
@@ -457,7 +457,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('taxonomy_vocabulary_delete')]
public function taxonomyVocabularyDelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_delete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_taxonomy_vocabulary_delete called';
}
/**
@@ -465,7 +465,7 @@ class EntityCrudHookTestHooks {
*/
#[Hook('user_delete')]
public function userDelete(): void {
- $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_delete' . ' called';
+ $GLOBALS['entity_crud_hook_test'][] = 'entity_crud_hook_test_user_delete called';
}
}
diff --git a/core/modules/system/tests/modules/experimental_module_requirements_test/experimental_module_requirements_test.install b/core/modules/system/tests/modules/experimental_module_requirements_test/experimental_module_requirements_test.install
deleted file mode 100644
index beaa3cd15b7..00000000000
--- a/core/modules/system/tests/modules/experimental_module_requirements_test/experimental_module_requirements_test.install
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/**
- * @file
- * Experimental Test Requirements module to test hook_requirements().
- */
-
-declare(strict_types=1);
-
-/**
- * Implements hook_requirements().
- */
-function experimental_module_requirements_test_requirements(): array {
- $requirements = [];
- if (\Drupal::state()->get('experimental_module_requirements_test_requirements', FALSE)) {
- $requirements['experimental_module_requirements_test_requirements'] = [
- 'severity' => REQUIREMENT_ERROR,
- 'description' => t('The Experimental Test Requirements module can not be installed.'),
- ];
- }
- return $requirements;
-}
diff --git a/core/modules/system/tests/modules/experimental_module_requirements_test/src/Install/Requirements/ExperimentalModuleRequirementsTestRequirements.php b/core/modules/system/tests/modules/experimental_module_requirements_test/src/Install/Requirements/ExperimentalModuleRequirementsTestRequirements.php
new file mode 100644
index 00000000000..53834f77c0e
--- /dev/null
+++ b/core/modules/system/tests/modules/experimental_module_requirements_test/src/Install/Requirements/ExperimentalModuleRequirementsTestRequirements.php
@@ -0,0 +1,29 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\experimental_module_requirements_test\Install\Requirements;
+
+use Drupal\Core\Extension\InstallRequirementsInterface;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
+
+/**
+ * Install time requirements for the Experimental Requirements Test module.
+ */
+class ExperimentalModuleRequirementsTestRequirements implements InstallRequirementsInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getRequirements(): array {
+ $requirements = [];
+ if (\Drupal::state()->get('experimental_module_requirements_test_requirements', FALSE)) {
+ $requirements['experimental_module_requirements_test_requirements'] = [
+ 'severity' => RequirementSeverity::Error,
+ 'description' => t('The Experimental Test Requirements module can not be installed.'),
+ ];
+ }
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestClickedButtonForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestClickedButtonForm.php
index 542c4e162e2..78328f9f8e4 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestClickedButtonForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestClickedButtonForm.php
@@ -35,32 +35,28 @@ class FormTestClickedButtonForm extends FormBase {
'#type' => 'textfield',
];
+ // Get button configurations, filter out NULL values.
+ $args = array_filter([$first, $second, $third]);
+
+ // Define button types for each argument.
+ $button_types = [
+ 's' => 'submit',
+ 'i' => 'image_button',
+ 'b' => 'button',
+ ];
+
// Loop through each path argument, adding buttons based on the information
// in the argument. For example, if the path is
// form-test/clicked-button/s/i/rb, then 3 buttons are added: a 'submit', an
// 'image_button', and a 'button' with #access=FALSE. This enables form.test
// to test a variety of combinations.
- $i = 0;
- $args = [$first, $second, $third];
- foreach ($args as $arg) {
- $name = 'button' . ++$i;
- // 's', 'b', or 'i' in the argument define the button type wanted.
- if (!is_string($arg)) {
- $type = NULL;
- }
- elseif (str_contains($arg, 's')) {
- $type = 'submit';
- }
- elseif (str_contains($arg, 'b')) {
- $type = 'button';
- }
- elseif (str_contains($arg, 'i')) {
- $type = 'image_button';
- }
- else {
- $type = NULL;
- }
- if (isset($type)) {
+ foreach ($args as $index => $arg) {
+ // Get the button type based on the index of the argument.
+ $type = $button_types[$arg] ?? NULL;
+ $name = 'button' . ($index + 1);
+
+ if ($type) {
+ // Define the button.
$form[$name] = [
'#type' => $type,
'#name' => $name,
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestGroupDetailsForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestGroupDetailsForm.php
index 9babda83ddc..226eb705802 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestGroupDetailsForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestGroupDetailsForm.php
@@ -48,6 +48,11 @@ class FormTestGroupDetailsForm extends FormBase {
'data-summary-attribute' => 'test',
],
];
+ $form['description_attributes'] = [
+ '#type' => 'details',
+ '#title' => 'Details element with description',
+ '#description' => 'I am a details description',
+ ];
return $form;
}
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestOptionalContainerForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestOptionalContainerForm.php
index 62157258cea..314d4675182 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestOptionalContainerForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestOptionalContainerForm.php
@@ -37,7 +37,7 @@ class FormTestOptionalContainerForm extends FormBase {
'#optional' => FALSE,
];
- // Non-empty containers
+ // Non-empty containers.
$form['nonempty_optional'] = [
'#type' => 'container',
'#attributes' => ['class' => ['nonempty_optional']],
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestStorageForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestStorageForm.php
index 637399b3638..cde0058a5db 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestStorageForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestStorageForm.php
@@ -34,7 +34,7 @@ class FormTestStorageForm extends FormBase {
if ($form_state->isRebuilding()) {
$form_state->setUserInput([]);
}
- // Initialize
+ // Initialize.
$storage = $form_state->getStorage();
$session = $this->getRequest()->getSession();
if (empty($storage)) {
@@ -42,7 +42,7 @@ class FormTestStorageForm extends FormBase {
if (empty($user_input)) {
$session->set('constructions', 0);
}
- // Put the initial thing into the storage
+ // Put the initial thing into the storage.
$storage = [
'thing' => [
'title' => 'none',
diff --git a/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php b/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php
index 6de8e4ad185..b6b54647efa 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php
@@ -446,7 +446,7 @@ class JavascriptStatesForm extends FormBase {
'#title' => 'Textfield in details',
];
- // Select trigger
+ // Select trigger.
$form['header_select'] = [
'#type' => 'html_tag',
'#tag' => 'h3',
diff --git a/core/modules/system/tests/modules/form_test/src/Hook/FormTestHooks.php b/core/modules/system/tests/modules/form_test/src/Hook/FormTestHooks.php
index cda2b92b347..441ebaa1d12 100644
--- a/core/modules/system/tests/modules/form_test/src/Hook/FormTestHooks.php
+++ b/core/modules/system/tests/modules/form_test/src/Hook/FormTestHooks.php
@@ -6,6 +6,8 @@ namespace Drupal\form_test\Hook;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Render\Element\Submit;
+use Drupal\Core\Render\ElementInfoManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\form_test\Callbacks;
@@ -16,6 +18,8 @@ class FormTestHooks {
use StringTranslationTrait;
+ public function __construct(protected ElementInfoManagerInterface $elementInfoManager) {}
+
/**
* Implements hook_form_FORM_ID_alter().
*/
@@ -55,13 +59,10 @@ class FormTestHooks {
*/
#[Hook('form_user_register_form_alter')]
public function formUserRegisterFormAlter(&$form, FormStateInterface $form_state) : void {
- $form['test_rebuild'] = [
- '#type' => 'submit',
- '#value' => $this->t('Rebuild'),
- '#submit' => [
- [Callbacks::class, 'userRegisterFormRebuild'],
- ],
- ];
+ $submit = $this->elementInfoManager->fromRenderable($form)
+ ->createChild('test_rebuild', Submit::class);
+ $submit->value = $this->t('Rebuild');
+ $submit->submit = [[Callbacks::class, 'userRegisterFormRebuild']];
}
/**
@@ -69,11 +70,12 @@ class FormTestHooks {
*/
#[Hook('form_form_test_vertical_tabs_access_form_alter')]
public function formFormTestVerticalTabsAccessFormAlter(&$form, &$form_state, $form_id) : void {
- $form['vertical_tabs1']['#access'] = FALSE;
- $form['vertical_tabs2']['#access'] = FALSE;
- $form['tabs3']['#access'] = TRUE;
- $form['fieldset1']['#access'] = FALSE;
- $form['container']['#access'] = FALSE;
+ $element_object = $this->elementInfoManager->fromRenderable($form);
+ $element_object->getChild('vertical_tabs1')->access = FALSE;
+ $element_object->getChild('vertical_tabs2')->access = FALSE;
+ $element_object->getChild('tab3')->access = FALSE;
+ $element_object->getChild('fieldset1')->access = FALSE;
+ $element_object->getChild('container')->access = FALSE;
}
}
diff --git a/core/modules/system/tests/modules/icon_test/config/schema/icon_test.schema.yml b/core/modules/system/tests/modules/icon_test/config/schema/icon_test.schema.yml
index 113b165e50e..ed96b94968d 100644
--- a/core/modules/system/tests/modules/icon_test/config/schema/icon_test.schema.yml
+++ b/core/modules/system/tests/modules/icon_test/config/schema/icon_test.schema.yml
@@ -8,14 +8,12 @@ icon.icon_pack_options.test_path:
type: integer
label: 'Width'
constraints:
- Range:
- min: 0
+ PositiveOrZero: ~
height:
type: integer
label: 'Height'
constraints:
- Range:
- min: 0
+ PositiveOrZero: ~
icon.icon_pack_options.test_svg:
type: mapping
@@ -27,8 +25,7 @@ icon.icon_pack_options.test_svg:
type: integer
label: 'Size'
constraints:
- Range:
- min: 0
+ PositiveOrZero: ~
icon.icon_pack_options.test_svg_sprite:
type: mapping
@@ -40,14 +37,12 @@ icon.icon_pack_options.test_svg_sprite:
type: integer
label: 'Width'
constraints:
- Range:
- min: 0
+ PositiveOrZero: ~
height:
type: integer
label: 'Height'
constraints:
- Range:
- min: 0
+ PositiveOrZero: ~
alt:
type: label
label: 'Alt'
@@ -62,14 +57,12 @@ icon.icon_pack_options.test_settings:
type: integer
label: 'Width'
constraints:
- Range:
- min: 0
+ PositiveOrZero: ~
height:
type: integer
label: 'Height'
constraints:
- Range:
- min: 0
+ PositiveOrZero: ~
title:
type: label
label: 'Title'
diff --git a/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/TestToolkit.php b/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/TestToolkit.php
index 908d0d8d454..09dbf982cf7 100644
--- a/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/TestToolkit.php
+++ b/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/TestToolkit.php
@@ -253,7 +253,11 @@ class TestToolkit extends ImageToolkitBase {
* IMAGETYPE_* constant (e.g. IMAGETYPE_JPEG, IMAGETYPE_PNG, etc.).
*/
protected static function supportedTypes() {
- return [IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF];
+ $types = [IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF];
+ if (\Drupal::keyValue('image_test')->get('avif_enabled', FALSE)) {
+ $types[] = IMAGETYPE_AVIF;
+ }
+ return $types;
}
/**
diff --git a/core/modules/system/tests/modules/js_displace/js_displace.module b/core/modules/system/tests/modules/js_displace/js_displace.module
deleted file mode 100644
index 8b34072bd65..00000000000
--- a/core/modules/system/tests/modules/js_displace/js_displace.module
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-/**
- * @file
- * Functions to support testing Drupal.displace() JavaScript API.
- */
-
-declare(strict_types=1);
-
-/**
- * Implements hook_preprocess_html().
- */
-function js_displace_preprocess_html(&$variables): void {
- $variables['#attached']['library'][] = 'core/drupal.displace';
-}
diff --git a/core/modules/system/tests/modules/js_displace/src/Hook/JsDisplaceThemeHooks.php b/core/modules/system/tests/modules/js_displace/src/Hook/JsDisplaceThemeHooks.php
new file mode 100644
index 00000000000..d9b37274d46
--- /dev/null
+++ b/core/modules/system/tests/modules/js_displace/src/Hook/JsDisplaceThemeHooks.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\js_displace\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Theme hook implementations for js_displace module.
+ */
+class JsDisplaceThemeHooks {
+
+ /**
+ * Implements hook_preprocess_HOOK().
+ */
+ #[Hook('preprocess_html')]
+ public function preprocessHtml(&$variables): void {
+ $variables['#attached']['library'][] = 'core/drupal.displace';
+ }
+
+}
diff --git a/core/modules/system/tests/modules/layout_test/layout_test.module b/core/modules/system/tests/modules/layout_test/layout_test.module
deleted file mode 100644
index 18459369b0a..00000000000
--- a/core/modules/system/tests/modules/layout_test/layout_test.module
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-/**
- * @file
- * Provides hook implementations for Layout Test.
- */
-
-declare(strict_types=1);
-
-/**
- * Implements hook_preprocess_HOOK() for layout templates.
- */
-function template_preprocess_layout_test_2col(&$variables): void {
- $variables['region_attributes']['left']->addClass('class-added-by-preprocess');
-}
diff --git a/core/modules/system/tests/modules/layout_test/src/Hook/LayoutTestThemeHooks.php b/core/modules/system/tests/modules/layout_test/src/Hook/LayoutTestThemeHooks.php
new file mode 100644
index 00000000000..35774e5de4b
--- /dev/null
+++ b/core/modules/system/tests/modules/layout_test/src/Hook/LayoutTestThemeHooks.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\layout_test\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for layout_test.
+ */
+class LayoutTestThemeHooks {
+
+ /**
+ * Implements hook_preprocess_HOOK() for layout templates.
+ */
+ #[Hook('preprocess_layout_test_2col')]
+ public function templatePreprocessLayoutTest2col(&$variables): void {
+ $variables['region_attributes']['left']->addClass('class-added-by-preprocess');
+ }
+
+}
diff --git a/core/modules/system/tests/modules/module_install_unmet_requirements/src/Install/Requirements/ModuleInstallUnmetRequirementsRequirements.php b/core/modules/system/tests/modules/module_install_unmet_requirements/src/Install/Requirements/ModuleInstallUnmetRequirementsRequirements.php
index 4d7367ff414..b6a532a1802 100644
--- a/core/modules/system/tests/modules/module_install_unmet_requirements/src/Install/Requirements/ModuleInstallUnmetRequirementsRequirements.php
+++ b/core/modules/system/tests/modules/module_install_unmet_requirements/src/Install/Requirements/ModuleInstallUnmetRequirementsRequirements.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Drupal\module_install_unmet_requirements\Install\Requirements;
use Drupal\Core\Extension\InstallRequirementsInterface;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
/**
* Provides method for checking requirements during install time.
@@ -17,7 +18,7 @@ class ModuleInstallUnmetRequirementsRequirements implements InstallRequirementsI
public static function getRequirements(): array {
$requirements['testing_requirements'] = [
'title' => t('Testing requirements'),
- 'severity' => REQUIREMENT_ERROR,
+ 'severity' => RequirementSeverity::Error,
'description' => t('Testing requirements failed requirements.'),
];
diff --git a/core/modules/system/tests/modules/module_runtime_requirements/src/Hook/ModuleRuntimeRequirementsHooks.php b/core/modules/system/tests/modules/module_runtime_requirements/src/Hook/ModuleRuntimeRequirementsHooks.php
index 0fa4b2f6f80..31358a595d7 100644
--- a/core/modules/system/tests/modules/module_runtime_requirements/src/Hook/ModuleRuntimeRequirementsHooks.php
+++ b/core/modules/system/tests/modules/module_runtime_requirements/src/Hook/ModuleRuntimeRequirementsHooks.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Drupal\module_runtime_requirements\Hook;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\StringTranslation\StringTranslationTrait;
@@ -24,13 +25,13 @@ class ModuleRuntimeRequirementsHooks {
'title' => $this->t('RuntimeError'),
'value' => $this->t('None'),
'description' => $this->t('Runtime Error.'),
- 'severity' => REQUIREMENT_ERROR,
+ 'severity' => RequirementSeverity::Error,
],
'test.runtime.error.alter' => [
'title' => $this->t('RuntimeError'),
'value' => $this->t('None'),
'description' => $this->t('Runtime Error.'),
- 'severity' => REQUIREMENT_ERROR,
+ 'severity' => RequirementSeverity::Error,
],
];
}
@@ -44,7 +45,7 @@ class ModuleRuntimeRequirementsHooks {
'title' => $this->t('RuntimeWarning'),
'value' => $this->t('None'),
'description' => $this->t('Runtime Warning.'),
- 'severity' => REQUIREMENT_WARNING,
+ 'severity' => RequirementSeverity::Warning,
];
}
diff --git a/core/modules/system/tests/modules/module_test/module_test.file.inc b/core/modules/system/tests/modules/module_test/module_test.file.inc
deleted file mode 100644
index c0cee6f1905..00000000000
--- a/core/modules/system/tests/modules/module_test/module_test.file.inc
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-/**
- * @file
- * Install, update and uninstall functions for the module_test module.
- *
- * Provides a hook to test \Drupal::moduleHandler()->getImplementationInfo()
- * loading includes.
- */
-
-declare(strict_types=1);
-
-/**
- * Implements hook_test_hook().
- */
-function module_test_test_hook(): array {
- return ['module_test' => 'success!'];
-}
diff --git a/core/modules/system/tests/modules/module_test/module_test.module b/core/modules/system/tests/modules/module_test/module_test.module
index 35561f09c8a..b3f3e131a58 100644
--- a/core/modules/system/tests/modules/module_test/module_test.module
+++ b/core/modules/system/tests/modules/module_test/module_test.module
@@ -7,57 +7,6 @@
declare(strict_types=1);
-use Drupal\Core\Extension\Extension;
-
-/**
- * Implements hook_system_info_alter().
- *
- * Manipulate module dependencies to test dependency chains.
- */
-function module_test_system_info_alter(&$info, Extension $file, $type): void {
- if (\Drupal::state()->get('module_test.dependency') == 'missing dependency') {
- if ($file->getName() == 'dblog') {
- // Make dblog module depend on config.
- $info['dependencies'][] = 'config';
- }
- elseif ($file->getName() == 'config') {
- // Make config module depend on a non-existing module.
- $info['dependencies'][] = 'foo';
- }
- }
- elseif (\Drupal::state()->get('module_test.dependency') == 'dependency') {
- if ($file->getName() == 'dblog') {
- // Make dblog module depend on config.
- $info['dependencies'][] = 'config';
- }
- elseif ($file->getName() == 'config') {
- // Make config module depend on help module.
- $info['dependencies'][] = 'help';
- }
- elseif ($file->getName() == 'entity_test') {
- // Make entity test module depend on help module.
- $info['dependencies'][] = 'help';
- }
- }
- elseif (\Drupal::state()->get('module_test.dependency') == 'version dependency') {
- if ($file->getName() == 'dblog') {
- // Make dblog module depend on config.
- $info['dependencies'][] = 'config';
- }
- elseif ($file->getName() == 'config') {
- // Make config module depend on a specific version of help module.
- $info['dependencies'][] = 'help (1.x)';
- }
- elseif ($file->getName() == 'help') {
- // Set help module to a version compatible with the above.
- $info['version'] = '8.x-1.0';
- }
- }
- if ($file->getName() == 'stark' && $type == 'theme') {
- $info['regions']['test_region'] = 'Test region';
- }
-}
-
/**
* Implements hook_hook_info().
*/
@@ -77,21 +26,3 @@ function module_test_load($param) {
$result = \Drupal::moduleHandler()->invokeAll('test_hook');
return $result[$param];
}
-
-/**
- * Implements hook_modules_installed().
- */
-function module_test_modules_installed($modules): void {
- // Record the ordered list of modules that were passed in to this hook so we
- // can check that the modules were enabled in the correct sequence.
- \Drupal::state()->set('module_test.install_order', $modules);
-}
-
-/**
- * Implements hook_modules_uninstalled().
- */
-function module_test_modules_uninstalled($modules): void {
- // Record the ordered list of modules that were passed in to this hook so we
- // can check that the modules were uninstalled in the correct sequence.
- \Drupal::state()->set('module_test.uninstall_order', $modules);
-}
diff --git a/core/modules/system/tests/modules/module_test/src/Hook/ModuleTestFileThemeHooks.php b/core/modules/system/tests/modules/module_test/src/Hook/ModuleTestFileThemeHooks.php
new file mode 100644
index 00000000000..381a9458260
--- /dev/null
+++ b/core/modules/system/tests/modules/module_test/src/Hook/ModuleTestFileThemeHooks.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\module_test\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for module_test.
+ */
+class ModuleTestFileThemeHooks {
+
+ /**
+ * Implements hook_test_hook().
+ */
+ #[Hook('test_hook')]
+ public function testHook(): array {
+ return [
+ 'module_test' => 'success!',
+ ];
+ }
+
+ /**
+ * Implements hook_test_reset_implementations_hook().
+ */
+ #[Hook('test_reset_implementations_hook')]
+ public function testResetImplementationsHook(): string {
+ return 'module_test_test_reset_implementations_hook';
+ }
+
+ /**
+ * Implements hook_test_reset_implementations_alter().
+ */
+ #[Hook('test_reset_implementations_alter')]
+ public function testResetImplementationsAlter(array &$data): void {
+ $data[] = 'module_test_test_reset_implementations_alter';
+ }
+
+}
diff --git a/core/modules/system/tests/modules/module_test/src/Hook/ModuleTestThemeHooks.php b/core/modules/system/tests/modules/module_test/src/Hook/ModuleTestThemeHooks.php
new file mode 100644
index 00000000000..a34adbe620c
--- /dev/null
+++ b/core/modules/system/tests/modules/module_test/src/Hook/ModuleTestThemeHooks.php
@@ -0,0 +1,85 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\module_test\Hook;
+
+use Drupal\Core\Extension\Extension;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for module_test.
+ */
+class ModuleTestThemeHooks {
+
+ /**
+ * Implements hook_system_info_alter().
+ *
+ * Manipulate module dependencies to test dependency chains.
+ */
+ #[Hook('system_info_alter')]
+ public function systemInfoAlter(&$info, Extension $file, $type): void {
+ if (\Drupal::state()->get('module_test.dependency') == 'missing dependency') {
+ if ($file->getName() == 'dblog') {
+ // Make dblog module depend on config.
+ $info['dependencies'][] = 'config';
+ }
+ elseif ($file->getName() == 'config') {
+ // Make config module depend on a non-existing module.
+ $info['dependencies'][] = 'foo';
+ }
+ }
+ elseif (\Drupal::state()->get('module_test.dependency') == 'dependency') {
+ if ($file->getName() == 'dblog') {
+ // Make dblog module depend on config.
+ $info['dependencies'][] = 'config';
+ }
+ elseif ($file->getName() == 'config') {
+ // Make config module depend on help module.
+ $info['dependencies'][] = 'help';
+ }
+ elseif ($file->getName() == 'entity_test') {
+ // Make entity test module depend on help module.
+ $info['dependencies'][] = 'help';
+ }
+ }
+ elseif (\Drupal::state()->get('module_test.dependency') == 'version dependency') {
+ if ($file->getName() == 'dblog') {
+ // Make dblog module depend on config.
+ $info['dependencies'][] = 'config';
+ }
+ elseif ($file->getName() == 'config') {
+ // Make config module depend on a specific version of help module.
+ $info['dependencies'][] = 'help (1.x)';
+ }
+ elseif ($file->getName() == 'help') {
+ // Set help module to a version compatible with the above.
+ $info['version'] = '8.x-1.0';
+ }
+ }
+ if ($file->getName() == 'stark' && $type == 'theme') {
+ $info['regions']['test_region'] = 'Test region';
+ }
+ }
+
+ /**
+ * Implements hook_modules_installed().
+ */
+ #[Hook('modules_installed')]
+ public function modulesInstalled($modules): void {
+ // Record the ordered list of modules that were passed in to this hook so we
+ // can check that the modules were enabled in the correct sequence.
+ \Drupal::state()->set('module_test.install_order', $modules);
+ }
+
+ /**
+ * Implements hook_modules_uninstalled().
+ */
+ #[Hook('modules_uninstalled')]
+ public function modulesUninstalled($modules): void {
+ // Record the ordered list of modules that were passed in to this hook so we
+ // can check that the modules were uninstalled in the correct sequence.
+ \Drupal::state()->set('module_test.uninstall_order', $modules);
+ }
+
+}
diff --git a/core/modules/system/tests/modules/module_test_oop_preprocess/src/Hook/ModuleTestOopPreprocessThemeHooks.php b/core/modules/system/tests/modules/module_test_oop_preprocess/src/Hook/ModuleTestOopPreprocessThemeHooks.php
index 1cbb9e6b422..db923382a21 100644
--- a/core/modules/system/tests/modules/module_test_oop_preprocess/src/Hook/ModuleTestOopPreprocessThemeHooks.php
+++ b/core/modules/system/tests/modules/module_test_oop_preprocess/src/Hook/ModuleTestOopPreprocessThemeHooks.php
@@ -4,19 +4,19 @@ declare(strict_types=1);
namespace Drupal\module_test_oop_preprocess\Hook;
-use Drupal\Core\Hook\Attribute\Preprocess;
+use Drupal\Core\Hook\Attribute\Hook;
/**
* Hook implementations for module_test_oop_preprocess.
*/
class ModuleTestOopPreprocessThemeHooks {
- #[Preprocess]
+ #[Hook('preprocess')]
public function rootPreprocess($arg): mixed {
return $arg;
}
- #[Preprocess('test')]
+ #[Hook('preprocess_test')]
public function preprocessTest($arg): mixed {
return $arg;
}
diff --git a/core/modules/system/tests/modules/module_update_requirements/src/Hook/ModuleUpdateRequirementsHooks.php b/core/modules/system/tests/modules/module_update_requirements/src/Hook/ModuleUpdateRequirementsHooks.php
index f0666222f14..073baba95c9 100644
--- a/core/modules/system/tests/modules/module_update_requirements/src/Hook/ModuleUpdateRequirementsHooks.php
+++ b/core/modules/system/tests/modules/module_update_requirements/src/Hook/ModuleUpdateRequirementsHooks.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Drupal\module_update_requirements\Hook;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\StringTranslation\StringTranslationTrait;
@@ -24,13 +25,13 @@ class ModuleUpdateRequirementsHooks {
'title' => $this->t('UpdateError'),
'value' => $this->t('None'),
'description' => $this->t('Update Error.'),
- 'severity' => REQUIREMENT_ERROR,
+ 'severity' => RequirementSeverity::Error,
],
'test.update.error.alter' => [
'title' => $this->t('UpdateError'),
'value' => $this->t('None'),
'description' => $this->t('Update Error.'),
- 'severity' => REQUIREMENT_ERROR,
+ 'severity' => RequirementSeverity::Error,
],
];
}
@@ -44,7 +45,7 @@ class ModuleUpdateRequirementsHooks {
'title' => $this->t('UpdateWarning'),
'value' => $this->t('None'),
'description' => $this->t('Update Warning.'),
- 'severity' => REQUIREMENT_WARNING,
+ 'severity' => RequirementSeverity::Warning,
];
}
diff --git a/core/modules/system/tests/modules/olivero_test/olivero_test.module b/core/modules/system/tests/modules/olivero_test/olivero_test.module
deleted file mode 100644
index 5d697759254..00000000000
--- a/core/modules/system/tests/modules/olivero_test/olivero_test.module
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-/**
- * @file
- * Functions to support testing the Olivero theme.
- */
-
-declare(strict_types=1);
-
-/**
- * Implements hook_preprocess_field_multiple_value_form().
- */
-function olivero_test_preprocess_field_multiple_value_form(&$variables): void {
- // Set test multiple value form field to disabled
- if ($variables["element"]["#field_name"] === "field_multiple_value_form_field") {
- $variables['element']['#disabled'] = TRUE;
- }
-}
-
-/**
- * Implements hook_preprocess_html().
- */
-function olivero_test_preprocess_html(&$variables): void {
- $variables['#attached']['library'][] = 'olivero_test/log-errors';
-}
diff --git a/core/modules/system/tests/modules/olivero_test/src/Hook/OliveroTestThemeHooks.php b/core/modules/system/tests/modules/olivero_test/src/Hook/OliveroTestThemeHooks.php
new file mode 100644
index 00000000000..1a5f7247a74
--- /dev/null
+++ b/core/modules/system/tests/modules/olivero_test/src/Hook/OliveroTestThemeHooks.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\olivero_test\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for olivero_test.
+ */
+class OliveroTestThemeHooks {
+
+ /**
+ * Implements hook_preprocess_field_multiple_value_form().
+ */
+ #[Hook('preprocess_field_multiple_value_form')]
+ public function preprocessFieldMultipleValueForm(&$variables): void {
+ // Set test multiple value form field to disabled.
+ if ($variables["element"]["#field_name"] === "field_multiple_value_form_field") {
+ $variables['element']['#disabled'] = TRUE;
+ }
+ }
+
+ /**
+ * Implements hook_preprocess_html().
+ */
+ #[Hook('preprocess_html')]
+ public function preprocessHtml(&$variables): void {
+ $variables['#attached']['library'][] = 'olivero_test/log-errors';
+ }
+
+}
diff --git a/core/modules/system/tests/modules/pager_test/pager_test.module b/core/modules/system/tests/modules/pager_test/pager_test.module
deleted file mode 100644
index e4556eddba3..00000000000
--- a/core/modules/system/tests/modules/pager_test/pager_test.module
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-/**
- * @file
- * Hook implementations for this module.
- */
-
-declare(strict_types=1);
-
-/**
- * Implements hook_preprocess_HOOK().
- */
-function pager_test_preprocess_pager(&$variables): void {
- // Nothing to do if there is only one page.
- $element = $variables['pager']['#element'];
- /** @var \Drupal\Core\Pager\PagerManagerInterface $pager_manager */
- $pager_manager = \Drupal::service('pager.manager');
- $pager = $pager_manager->getPager($element);
-
- // Nothing to do if there is no pager.
- if (!isset($pager)) {
- return;
- }
-
- // Nothing to do if there is only one page.
- if ($pager->getTotalPages() <= 1) {
- return;
- }
-
- foreach ($variables['items']['pages'] as &$pager_item) {
- $pager_item['attributes']['pager-test'] = 'yes';
- $pager_item['attributes']->addClass('lizards');
- }
- unset($pager_item);
-
- foreach (['first', 'previous', 'next', 'last'] as $special_pager_item) {
- if (isset($variables['items'][$special_pager_item])) {
- $variables['items'][$special_pager_item]['attributes']->addClass('lizards');
- $variables['items'][$special_pager_item]['attributes']['pager-test'] = $special_pager_item;
- }
- }
-}
diff --git a/core/modules/system/tests/modules/pager_test/src/Hook/PagerTestThemeHooks.php b/core/modules/system/tests/modules/pager_test/src/Hook/PagerTestThemeHooks.php
new file mode 100644
index 00000000000..ae72036a552
--- /dev/null
+++ b/core/modules/system/tests/modules/pager_test/src/Hook/PagerTestThemeHooks.php
@@ -0,0 +1,50 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\pager_test\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for pager_test.
+ */
+class PagerTestThemeHooks {
+
+ /**
+ * Implements hook_preprocess_HOOK().
+ */
+ #[Hook('preprocess_pager')]
+ public function preprocessPager(&$variables): void {
+ // Nothing to do if there is only one page.
+ $element = $variables['pager']['#element'];
+ /** @var \Drupal\Core\Pager\PagerManagerInterface $pager_manager */
+ $pager_manager = \Drupal::service('pager.manager');
+ $pager = $pager_manager->getPager($element);
+ // Nothing to do if there is no pager.
+ if (!isset($pager)) {
+ return;
+ }
+ // Nothing to do if there is only one page.
+ if ($pager->getTotalPages() <= 1) {
+ return;
+ }
+ foreach ($variables['items']['pages'] as &$pager_item) {
+ $pager_item['attributes']['pager-test'] = 'yes';
+ $pager_item['attributes']->addClass('lizards');
+ }
+ unset($pager_item);
+ foreach ([
+ 'first',
+ 'previous',
+ 'next',
+ 'last',
+ ] as $special_pager_item) {
+ if (isset($variables['items'][$special_pager_item])) {
+ $variables['items'][$special_pager_item]['attributes']->addClass('lizards');
+ $variables['items'][$special_pager_item]['attributes']['pager-test'] = $special_pager_item;
+ }
+ }
+ }
+
+}
diff --git a/core/modules/system/tests/modules/requirements1_test/requirements1_test.install b/core/modules/system/tests/modules/requirements1_test/requirements1_test.install
index fb84be133cd..a93f726fafd 100644
--- a/core/modules/system/tests/modules/requirements1_test/requirements1_test.install
+++ b/core/modules/system/tests/modules/requirements1_test/requirements1_test.install
@@ -7,6 +7,8 @@
declare(strict_types=1);
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
+
/**
* Implements hook_requirements().
*
@@ -19,20 +21,20 @@ function requirements1_test_requirements($phase): array {
if ('install' == $phase) {
$requirements['requirements1_test'] = [
'title' => t('Requirements 1 Test'),
- 'severity' => REQUIREMENT_ERROR,
+ 'severity' => RequirementSeverity::Error,
'description' => t('Requirements 1 Test failed requirements.'),
];
}
$requirements['requirements1_test_alterable'] = [
'title' => t('Requirements 1 Test Alterable'),
- 'severity' => REQUIREMENT_ERROR,
+ 'severity' => RequirementSeverity::Error,
'description' => t('A requirement that will be altered.'),
];
$requirements['requirements1_test_deletable'] = [
'title' => t('Requirements 1 Test Deletable'),
- 'severity' => REQUIREMENT_INFO,
+ 'severity' => RequirementSeverity::Info,
'description' => t('A requirement that will be deleted.'),
];
diff --git a/core/modules/system/tests/modules/requirements1_test/src/Hook/Requirements1TestHooks.php b/core/modules/system/tests/modules/requirements1_test/src/Hook/Requirements1TestHooks.php
index c766f6f423a..ce3eebfb35b 100644
--- a/core/modules/system/tests/modules/requirements1_test/src/Hook/Requirements1TestHooks.php
+++ b/core/modules/system/tests/modules/requirements1_test/src/Hook/Requirements1TestHooks.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Drupal\requirements1_test\Hook;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\StringTranslation\StringTranslationTrait;
@@ -22,7 +23,7 @@ class Requirements1TestHooks {
// Change the title.
$requirements['requirements1_test_alterable']['title'] = $this->t('Requirements 1 Test - Changed');
// Decrease the severity.
- $requirements['requirements1_test_alterable']['severity'] = REQUIREMENT_WARNING;
+ $requirements['requirements1_test_alterable']['severity'] = RequirementSeverity::Warning;
// Delete 'requirements1_test_deletable',
unset($requirements['requirements1_test_deletable']);
}
diff --git a/core/modules/system/tests/modules/router_test_directory/router_test.module b/core/modules/system/tests/modules/router_test_directory/router_test.module
deleted file mode 100644
index 2158075059c..00000000000
--- a/core/modules/system/tests/modules/router_test_directory/router_test.module
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-/**
- * @file
- * Test module.
- */
-
-declare(strict_types=1);
-
-use Drupal\Core\Url;
-
-/**
- * Implements hook_preprocess_HOOK().
- *
- * Performs an operation that calls the RouteProvider's collection method
- * during an exception page view. (which is rendered during a subrequest.)
- *
- * @see \Drupal\FunctionalTests\Routing\RouteCachingQueryAlteredTest
- */
-function router_test_preprocess_page(&$variables): void {
- $request = \Drupal::request();
- if ($request->getPathInfo() === '/router-test/rejects-query-strings') {
- // Create a URL from the request, e.g. for a breadcrumb or other contextual
- // information.
- Url::createFromRequest($request);
- }
-}
diff --git a/core/modules/system/tests/modules/router_test_directory/src/Hook/RouterTestThemeHooks.php b/core/modules/system/tests/modules/router_test_directory/src/Hook/RouterTestThemeHooks.php
new file mode 100644
index 00000000000..da642b8b13b
--- /dev/null
+++ b/core/modules/system/tests/modules/router_test_directory/src/Hook/RouterTestThemeHooks.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\router_test\Hook;
+
+use Drupal\Core\Url;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for router_test.
+ */
+class RouterTestThemeHooks {
+
+ /**
+ * Implements hook_preprocess_HOOK().
+ *
+ * Performs an operation that calls the RouteProvider's collection method
+ * during an exception page view. (which is rendered during a subrequest.)
+ *
+ * @see \Drupal\FunctionalTests\Routing\RouteCachingQueryAlteredTest
+ */
+ #[Hook('preprocess_page')]
+ public function preprocessPage(&$variables): void {
+ $request = \Drupal::request();
+ if ($request->getPathInfo() === '/router-test/rejects-query-strings') {
+ // Create a URL from the request, e.g. for a breadcrumb or other contextual
+ // information.
+ Url::createFromRequest($request);
+ }
+ }
+
+}
diff --git a/core/modules/system/tests/modules/sdc_test/components/my-banner/my-banner.component.yml b/core/modules/system/tests/modules/sdc_test/components/my-banner/my-banner.component.yml
index 195903e9ee7..0c89a0740a6 100644
--- a/core/modules/system/tests/modules/sdc_test/components/my-banner/my-banner.component.yml
+++ b/core/modules/system/tests/modules/sdc_test/components/my-banner/my-banner.component.yml
@@ -29,6 +29,10 @@ props:
enum:
- ''
- _blank
+ meta:enum:
+ '': 'Open in same window'
+ _blank: 'Open in a new window'
+ x-translation-context: Banner link target
image:
title: Media Image
description: Background image for the banner.
diff --git a/core/modules/system/tests/modules/sdc_test/components/my-button/my-button.component.yml b/core/modules/system/tests/modules/sdc_test/components/my-button/my-button.component.yml
index 65b3c472096..d0d4f8c73b4 100644
--- a/core/modules/system/tests/modules/sdc_test/components/my-button/my-button.component.yml
+++ b/core/modules/system/tests/modules/sdc_test/components/my-button/my-button.component.yml
@@ -24,3 +24,7 @@ props:
- power
- like
- external
+ meta:enum:
+ power: 'Power'
+ like: 'Like'
+ external: 'External'
diff --git a/core/modules/system/tests/modules/sdc_test/components/my-cta/my-cta.component.yml b/core/modules/system/tests/modules/sdc_test/components/my-cta/my-cta.component.yml
index 889dfe88520..6d16be49cf7 100644
--- a/core/modules/system/tests/modules/sdc_test/components/my-cta/my-cta.component.yml
+++ b/core/modules/system/tests/modules/sdc_test/components/my-cta/my-cta.component.yml
@@ -17,12 +17,23 @@ props:
type: string
title: URL
format: uri
+ examples:
+ - https://drupal.org
target:
type: string
title: Target
+ description: The target for opening the link.
enum:
- ''
- - _blank
+ - '_blank'
+ meta:enum:
+ '': 'Open in same window'
+ _blank: 'Open in a new window'
+ x-translation-context: CTA link target
+ default: ''
+ examples:
+ - ''
+ - '_blank'
attributes:
type: Drupal\Core\Template\Attribute
name: Attributes
diff --git a/core/modules/system/tests/modules/sdc_test_replacements/components/my-button/my-button.component.yml b/core/modules/system/tests/modules/sdc_test_replacements/components/my-button/my-button.component.yml
index b87f1180111..053387cf9c4 100644
--- a/core/modules/system/tests/modules/sdc_test_replacements/components/my-button/my-button.component.yml
+++ b/core/modules/system/tests/modules/sdc_test_replacements/components/my-button/my-button.component.yml
@@ -24,3 +24,7 @@ props:
- power
- like
- external
+ meta:enum:
+ power: 'Power'
+ like: 'Like'
+ external: 'External'
diff --git a/core/modules/system/tests/modules/session_test/session_test.routing.yml b/core/modules/system/tests/modules/session_test/session_test.routing.yml
index fe85de11032..f11bd86b4d7 100644
--- a/core/modules/system/tests/modules/session_test/session_test.routing.yml
+++ b/core/modules/system/tests/modules/session_test/session_test.routing.yml
@@ -179,3 +179,25 @@ session_test.trigger_write_exception:
no_cache: TRUE
requirements:
_access: 'TRUE'
+
+session_test.legacy_get:
+ path: '/session-test/legacy-get'
+ defaults:
+ _title: 'Legacy session value'
+ _controller: '\Drupal\session_test\Controller\LegacySessionTestController::get'
+ options:
+ no_cache: TRUE
+ requirements:
+ _access: 'TRUE'
+
+session_test.legacy_set:
+ path: '/session-test/legacy-set/{test_value}'
+ defaults:
+ _title: 'Set legacy session value'
+ _controller: '\Drupal\session_test\Controller\LegacySessionTestController::set'
+ options:
+ no_cache: TRUE
+ converters:
+ test_value: '\s+'
+ requirements:
+ _access: 'TRUE'
diff --git a/core/modules/system/tests/modules/session_test/src/Controller/LegacySessionTestController.php b/core/modules/system/tests/modules/session_test/src/Controller/LegacySessionTestController.php
new file mode 100644
index 00000000000..a1438a0108e
--- /dev/null
+++ b/core/modules/system/tests/modules/session_test/src/Controller/LegacySessionTestController.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\session_test\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+
+/**
+ * Controller providing page callbacks for legacy session tests.
+ */
+class LegacySessionTestController extends ControllerBase {
+
+ /**
+ * Prints the stored session value to the screen.
+ */
+ public function get(): array {
+ return empty($_SESSION['legacy_test_value'])
+ ? []
+ : ['#markup' => $this->t('The current value of the stored session variable is: %val', ['%val' => $_SESSION['legacy_test_value']])];
+ }
+
+ /**
+ * Stores a value in $_SESSION['legacy_test_value'].
+ *
+ * @param string $test_value
+ * A session value.
+ */
+ public function set(string $test_value): array {
+ $_SESSION['legacy_test_value'] = $test_value;
+
+ return ['#markup' => $this->t('The current value of the stored session variable has been set to %val', ['%val' => $test_value])];
+ }
+
+}
diff --git a/core/modules/system/tests/modules/session_test/src/Controller/SessionTestController.php b/core/modules/system/tests/modules/session_test/src/Controller/SessionTestController.php
index 9c7bb97e24b..461581abaa7 100644
--- a/core/modules/system/tests/modules/session_test/src/Controller/SessionTestController.php
+++ b/core/modules/system/tests/modules/session_test/src/Controller/SessionTestController.php
@@ -11,20 +11,21 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
- * Controller providing page callbacks for the action admin interface.
+ * Controller providing page callbacks for session tests.
*/
class SessionTestController extends ControllerBase {
/**
* Prints the stored session value to the screen.
*
- * @return string
- * A notification message.
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The incoming request.
*/
- public function get() {
- return empty($_SESSION['session_test_value'])
+ public function get(Request $request): array {
+ $value = $request->getSession()->get('session_test_value');
+ return empty($value)
? []
- : ['#markup' => $this->t('The current value of the stored session variable is: %val', ['%val' => $_SESSION['session_test_value']])];
+ : ['#markup' => $this->t('The current value of the stored session variable is: %val', ['%val' => $value])];
}
/**
@@ -32,11 +33,8 @@ class SessionTestController extends ControllerBase {
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The incoming request.
- *
- * @return string
- * A notification message.
*/
- public function getFromSessionObject(Request $request) {
+ public function getFromSessionObject(Request $request): array {
$value = $request->getSession()->get("session_test_key");
return empty($value)
? []
@@ -48,16 +46,13 @@ class SessionTestController extends ControllerBase {
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The incoming request.
- *
- * @return string
- * A notification message with session ID.
*/
- public function getId(Request $request) {
- // Set a value in $_SESSION, so that SessionManager::save() will start
+ public function getId(Request $request): array {
+ // Set a value in session, so that SessionManager::save() will start
// a session.
- $_SESSION['test'] = 'test';
-
- $request->getSession()->save();
+ $session = $request->getSession();
+ $session->set('test', 'test');
+ $session->save();
return ['#markup' => 'session_id:' . session_id() . "\n"];
}
@@ -67,11 +62,8 @@ class SessionTestController extends ControllerBase {
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
- *
- * @return string
- * A notification message with session ID.
*/
- public function getIdFromCookie(Request $request) {
+ public function getIdFromCookie(Request $request): array {
return [
'#markup' => 'session_id:' . $request->cookies->get(session_name()) . "\n",
'#cache' => ['contexts' => ['cookies:' . session_name()]],
@@ -79,16 +71,15 @@ class SessionTestController extends ControllerBase {
}
/**
- * Stores a value in $_SESSION['session_test_value'].
+ * Stores a value in 'session_test_value' session attribute.
*
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The request object.
* @param string $test_value
* A session value.
- *
- * @return string
- * A notification message.
*/
- public function set($test_value) {
- $_SESSION['session_test_value'] = $test_value;
+ public function set(Request $request, $test_value): array {
+ $request->getSession()->set('session_test_value', $test_value);
return ['#markup' => $this->t('The current value of the stored session variable has been set to %val', ['%val' => $test_value])];
}
@@ -96,25 +87,21 @@ class SessionTestController extends ControllerBase {
/**
* Turns off session saving and then tries to save a value anyway.
*
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The request object.
* @param string $test_value
* A session value.
- *
- * @return string
- * A notification message.
*/
- public function noSet($test_value) {
+ public function noSet(Request $request, $test_value): array {
\Drupal::service('session_handler.write_safe')->setSessionWritable(FALSE);
- $this->set($test_value);
+ $this->set($request, $test_value);
return ['#markup' => $this->t('session saving was disabled, and then %val was set', ['%val' => $test_value])];
}
/**
* Sets a message to me displayed on the following page.
- *
- * @return string
- * A notification message.
*/
- public function setMessage() {
+ public function setMessage(): Response {
$this->messenger()->addStatus($this->t('This is a dummy message.'));
return new Response((string) $this->t('A message was set.'));
// Do not return anything, so the current request does not result in a
@@ -124,11 +111,8 @@ class SessionTestController extends ControllerBase {
/**
* Sets a message but call drupal_save_session(FALSE).
- *
- * @return string
- * A notification message.
*/
- public function setMessageButDoNotSave() {
+ public function setMessageButDoNotSave(): array {
\Drupal::service('session_handler.write_safe')->setSessionWritable(FALSE);
$this->setMessage();
return ['#markup' => ''];
@@ -136,11 +120,8 @@ class SessionTestController extends ControllerBase {
/**
* Only available if current user is logged in.
- *
- * @return string
- * A notification message.
*/
- public function isLoggedIn() {
+ public function isLoggedIn(): array {
return ['#markup' => $this->t('User is logged in.')];
}
@@ -149,20 +130,13 @@ class SessionTestController extends ControllerBase {
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The incoming request.
- *
- * @return \Symfony\Component\HttpFoundation\JsonResponse
- * The response.
*/
- public function traceHandler(Request $request) {
- // Start a session if necessary, set a value and then save and close it.
- $request->getSession()->start();
- if (empty($_SESSION['trace-handler'])) {
- $_SESSION['trace-handler'] = 1;
- }
- else {
- $_SESSION['trace-handler']++;
- }
- $request->getSession()->save();
+ public function traceHandler(Request $request): Response {
+ // Increment trace-handler counter and save the session.
+ $session = $request->getSession();
+ $counter = $session->get('trace-handler', 0);
+ $session->set('trace-handler', $counter + 1);
+ $session->save();
// Collect traces and return them in JSON format.
$trace = \Drupal::service('session_test.session_handler_proxy_trace')->getArrayCopy();
@@ -182,15 +156,13 @@ class SessionTestController extends ControllerBase {
* @param \Symfony\Component\HttpFoundation\Request $request
* The incoming request.
*
- * @return \Symfony\Component\HttpFoundation\JsonResponse
- * The response.
- *
* @throws \AssertionError
*/
- public function traceHandlerRewriteUnmodified(Request $request) {
+ public function traceHandlerRewriteUnmodified(Request $request): Response {
// Assert that there is an existing session with stacked handler trace data.
+ $session = $request->getSession();
assert(
- is_int($_SESSION['trace-handler']) && $_SESSION['trace-handler'] > 0,
+ is_int($session->get('trace-handler')) && $session->get('trace-handler') > 0,
'Existing stacked session handler trace not found'
);
@@ -199,7 +171,7 @@ class SessionTestController extends ControllerBase {
ini_get('session.lazy_write'),
'session.lazy_write must be enabled to invoke updateTimestamp()'
);
- $request->getSession()->save();
+ $session->save();
// Collect traces and return them in JSON format.
$trace = \Drupal::service('session_test.session_handler_proxy_trace')->getArrayCopy();
@@ -212,11 +184,8 @@ class SessionTestController extends ControllerBase {
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
- *
- * @return \Symfony\Component\HttpFoundation\JsonResponse
- * A response object containing the session values and the user ID.
*/
- public function getSession(Request $request) {
+ public function getSession(Request $request): Response {
return new JsonResponse(['session' => $request->getSession()->all(), 'user' => $this->currentUser()->id()]);
}
@@ -227,11 +196,8 @@ class SessionTestController extends ControllerBase {
* The request object.
* @param string $test_value
* A value to set on the session.
- *
- * @return \Symfony\Component\HttpFoundation\JsonResponse
- * A response object containing the session values and the user ID.
*/
- public function setSession(Request $request, $test_value) {
+ public function setSession(Request $request, $test_value): Response {
$session = $request->getSession();
$session->set('test_value', $test_value);
return new JsonResponse(['session' => $session->all(), 'user' => $this->currentUser()->id()]);
@@ -242,11 +208,8 @@ class SessionTestController extends ControllerBase {
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
- *
- * @return \Symfony\Component\HttpFoundation\Response
- * The response object.
*/
- public function setSessionBagFlag(Request $request) {
+ public function setSessionBagFlag(Request $request): Response {
/** @var \Drupal\session_test\Session\TestSessionBag */
$bag = $request->getSession()->getBag(TestSessionBag::BAG_NAME);
$bag->setFlag();
@@ -258,11 +221,8 @@ class SessionTestController extends ControllerBase {
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
- *
- * @return \Symfony\Component\HttpFoundation\Response
- * The response object.
*/
- public function clearSessionBagFlag(Request $request) {
+ public function clearSessionBagFlag(Request $request): Response {
/** @var \Drupal\session_test\Session\TestSessionBag */
$bag = $request->getSession()->getBag(TestSessionBag::BAG_NAME);
$bag->clearFlag();
@@ -274,11 +234,8 @@ class SessionTestController extends ControllerBase {
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
- *
- * @return \Symfony\Component\HttpFoundation\Response
- * The response object.
*/
- public function hasSessionBagFlag(Request $request) {
+ public function hasSessionBagFlag(Request $request): Response {
/** @var \Drupal\session_test\Session\TestSessionBag */
$bag = $request->getSession()->getBag(TestSessionBag::BAG_NAME);
return new Response(empty($bag->hasFlag())
@@ -293,7 +250,7 @@ class SessionTestController extends ControllerBase {
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
*/
- public function triggerWriteException(Request $request) {
+ public function triggerWriteException(Request $request): Response {
$session = $request->getSession();
$session->set('test_value', 'Ensure session contains some data');
diff --git a/core/modules/system/tests/modules/system_test/src/Controller/OptionalServiceSystemTestController.php b/core/modules/system/tests/modules/system_test/src/Controller/OptionalServiceSystemTestController.php
new file mode 100644
index 00000000000..b57e4c88397
--- /dev/null
+++ b/core/modules/system/tests/modules/system_test/src/Controller/OptionalServiceSystemTestController.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\system_test\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\dblog\Logger\DbLog;
+use Symfony\Component\DependencyInjection\Attribute\Autowire;
+
+/**
+ * A controller that specifies an optional dependency.
+ */
+class OptionalServiceSystemTestController extends ControllerBase {
+
+ public function __construct(
+ #[Autowire('logger.dblog')]
+ public readonly ?DbLog $dbLog,
+ ) {}
+
+}
diff --git a/core/modules/system/tests/modules/test_htmx/css/style.css b/core/modules/system/tests/modules/test_htmx/css/style.css
new file mode 100644
index 00000000000..75b757dbe3c
--- /dev/null
+++ b/core/modules/system/tests/modules/test_htmx/css/style.css
@@ -0,0 +1,3 @@
+.ajax-content {
+ background-color: red;
+}
diff --git a/core/modules/system/tests/modules/test_htmx/js/behavior.js b/core/modules/system/tests/modules/test_htmx/js/behavior.js
new file mode 100644
index 00000000000..5ca13501cee
--- /dev/null
+++ b/core/modules/system/tests/modules/test_htmx/js/behavior.js
@@ -0,0 +1,14 @@
+((Drupal, once) => {
+ Drupal.behaviors.htmx_test = {
+ attach(context, settings) {
+ once('htmx-init', '.ajax-content', context).forEach((el) => {
+ el.innerText = 'initialized';
+ });
+ },
+ detach(context, settings, trigger) {
+ once.remove('htmx-init', '.ajax-content', context).forEach((el) => {
+ el.remove();
+ });
+ },
+ };
+})(Drupal, once);
diff --git a/core/modules/system/tests/modules/test_htmx/src/Controller/HtmxTestAttachmentsController.php b/core/modules/system/tests/modules/test_htmx/src/Controller/HtmxTestAttachmentsController.php
new file mode 100644
index 00000000000..9027509a19c
--- /dev/null
+++ b/core/modules/system/tests/modules/test_htmx/src/Controller/HtmxTestAttachmentsController.php
@@ -0,0 +1,116 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\test_htmx\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Url;
+
+/**
+ * Returns responses for HTMX Test Attachments routes.
+ */
+final class HtmxTestAttachmentsController extends ControllerBase {
+
+ /**
+ * Builds the response.
+ *
+ * @return mixed[]
+ * A render array.
+ */
+ public function page(): array {
+ return self::generateHtmxButton();
+ }
+
+ /**
+ * Builds a response with a `beforebegin` swap.
+ *
+ * @return mixed[]
+ * A render array.
+ */
+ public function before(): array {
+ return self::generateHtmxButton('beforebegin');
+ }
+
+ /**
+ * Builds a response with an `afterend` swap..
+ *
+ * @return mixed[]
+ * A render array.
+ */
+ public function after(): array {
+ return self::generateHtmxButton('afterend');
+ }
+
+ /**
+ * Builds the HTMX response.
+ *
+ * @return mixed[]
+ * A render array.
+ */
+ public function replace(): array {
+ $build['content'] = [
+ '#type' => 'container',
+ '#attached' => [
+ 'library' => ['test_htmx/assets'],
+ ],
+ '#attributes' => [
+ 'class' => ['ajax-content'],
+ ],
+ 'example' => ['#markup' => 'Initial Content'],
+ ];
+
+ return $build;
+ }
+
+ /**
+ * We need a static callback that ignores callback parameters.
+ *
+ * @return array
+ * The render array.
+ */
+ public static function replaceWithAjax(): array {
+ return static::generateHtmxButton();
+ }
+
+ /**
+ * Static helper to for reusable render array.
+ *
+ * @return array
+ * The render array.
+ */
+ public static function generateHtmxButton(string $swap = ''): array {
+ $url = Url::fromRoute('test_htmx.attachments.replace');
+ $build['replace'] = [
+ '#type' => 'html_tag',
+ '#tag' => 'button',
+ '#attributes' => [
+ 'type' => 'button',
+ 'name' => 'replace',
+ 'data-hx-get' => $url->toString(),
+ 'data-hx-select' => 'div.ajax-content',
+ 'data-hx-target' => '[data-drupal-htmx-target]',
+ ],
+ '#value' => 'Click this',
+ '#attached' => [
+ 'library' => [
+ 'core/drupal.htmx',
+ ],
+ ],
+ ];
+ if ($swap !== '') {
+ $build['replace']['#attributes']['data-hx-swap'] = $swap;
+ }
+
+ $build['content'] = [
+ '#type' => 'container',
+ '#attributes' => [
+ 'data-drupal-htmx-target' => TRUE,
+ 'class' => ['htmx-test-container'],
+ ],
+ ];
+
+ return $build;
+ }
+
+}
diff --git a/core/modules/system/tests/modules/test_htmx/src/Form/HtmxTestAjaxForm.php b/core/modules/system/tests/modules/test_htmx/src/Form/HtmxTestAjaxForm.php
new file mode 100644
index 00000000000..f812a99582d
--- /dev/null
+++ b/core/modules/system/tests/modules/test_htmx/src/Form/HtmxTestAjaxForm.php
@@ -0,0 +1,51 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\test_htmx\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\test_htmx\Controller\HtmxTestAttachmentsController;
+
+/**
+ * A small form used to insert an HTMX powered element using ajax API.
+ */
+class HtmxTestAjaxForm extends FormBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFormId(): string {
+ return 'htmx_test_ajax_form';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(array $form, FormStateInterface $form_state): array {
+ $build = [
+ 'ajax-button' => [
+ '#type' => 'button',
+ '#value' => 'Trigger Ajax',
+ '#submit_button' => FALSE,
+ '#ajax' => [
+ 'callback' => [
+ HtmxTestAttachmentsController::class,
+ 'replaceWithAjax',
+ ],
+ 'wrapper' => 'ajax-test-container',
+ ],
+ ],
+ '#suffix' => '<div id="ajax-test-container"></div>',
+ ];
+
+ return $build;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, FormStateInterface $form_state): void {}
+
+}
diff --git a/core/modules/system/tests/modules/test_htmx/test_htmx.info.yml b/core/modules/system/tests/modules/test_htmx/test_htmx.info.yml
new file mode 100644
index 00000000000..c713e0624d9
--- /dev/null
+++ b/core/modules/system/tests/modules/test_htmx/test_htmx.info.yml
@@ -0,0 +1,4 @@
+name: 'HTMX Test Fixtures'
+type: module
+description: 'Test fixtures for HTMX integration'
+package: Testing
diff --git a/core/modules/system/tests/modules/test_htmx/test_htmx.libraries.yml b/core/modules/system/tests/modules/test_htmx/test_htmx.libraries.yml
new file mode 100644
index 00000000000..31ac1d2b8ab
--- /dev/null
+++ b/core/modules/system/tests/modules/test_htmx/test_htmx.libraries.yml
@@ -0,0 +1,10 @@
+assets:
+ version: VERSION
+ js:
+ js/behavior.js: {}
+ css:
+ theme:
+ css/style.css: {}
+ dependencies:
+ - core/drupal
+ - core/once
diff --git a/core/modules/system/tests/modules/test_htmx/test_htmx.routing.yml b/core/modules/system/tests/modules/test_htmx/test_htmx.routing.yml
new file mode 100644
index 00000000000..33dca377c71
--- /dev/null
+++ b/core/modules/system/tests/modules/test_htmx/test_htmx.routing.yml
@@ -0,0 +1,39 @@
+test_htmx.attachments.page:
+ path: '/htmx-test-attachments/page'
+ defaults:
+ _title: 'Page'
+ _controller: '\Drupal\test_htmx\Controller\HtmxTestAttachmentsController::page'
+ requirements:
+ _permission: 'access content'
+
+test_htmx.attachments.before:
+ path: '/htmx-test-attachments/before'
+ defaults:
+ _title: 'Page'
+ _controller: '\Drupal\test_htmx\Controller\HtmxTestAttachmentsController::before'
+ requirements:
+ _permission: 'access content'
+
+test_htmx.attachments.after:
+ path: '/htmx-test-attachments/after'
+ defaults:
+ _title: 'Page'
+ _controller: '\Drupal\test_htmx\Controller\HtmxTestAttachmentsController::after'
+ requirements:
+ _permission: 'access content'
+
+test_htmx.attachments.replace:
+ path: '/htmx-test-attachments/replace'
+ defaults:
+ _title: 'Ajax Content'
+ _controller: '\Drupal\test_htmx\Controller\HtmxTestAttachmentsController::replace'
+ requirements:
+ _permission: 'access content'
+
+test_htmx.attachments.ajax:
+ path: '/htmx-test-attachments/ajax'
+ defaults:
+ _title: 'Ajax'
+ _form: '\Drupal\test_htmx\Form\HtmxTestAjaxForm'
+ requirements:
+ _permission: 'access content'
diff --git a/core/modules/system/tests/modules/theme_region_test/src/Hook/ThemeRegionTestThemeHooks.php b/core/modules/system/tests/modules/theme_region_test/src/Hook/ThemeRegionTestThemeHooks.php
new file mode 100644
index 00000000000..5cff4c19531
--- /dev/null
+++ b/core/modules/system/tests/modules/theme_region_test/src/Hook/ThemeRegionTestThemeHooks.php
@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\theme_region_test\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for theme_region_test.
+ */
+class ThemeRegionTestThemeHooks {
+
+ /**
+ * Implements hook_preprocess_HOOK() for region templates.
+ */
+ #[Hook('preprocess_region')]
+ public function preprocessRegion(&$variables): void {
+ if ($variables['region'] == 'sidebar_first') {
+ $variables['attributes']['class'][] = 'new_class';
+ }
+ }
+
+}
diff --git a/core/modules/system/tests/modules/theme_region_test/theme_region_test.module b/core/modules/system/tests/modules/theme_region_test/theme_region_test.module
deleted file mode 100644
index 5e9dc670a47..00000000000
--- a/core/modules/system/tests/modules/theme_region_test/theme_region_test.module
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-/**
- * @file
- * Provides hook implementations for testing purposes.
- */
-
-declare(strict_types=1);
-
-/**
- * Implements hook_preprocess_HOOK() for region templates.
- */
-function theme_region_test_preprocess_region(&$variables): void {
- if ($variables['region'] == 'sidebar_first') {
- $variables['attributes']['class'][] = 'new_class';
- }
-}
diff --git a/core/modules/system/tests/modules/theme_suggestions_test/src/Hook/ThemeSuggestionsTestHooks.php b/core/modules/system/tests/modules/theme_suggestions_test/src/Hook/ThemeSuggestionsTestHooks.php
index 8f5d5ee18a8..ff4086a0c2a 100644
--- a/core/modules/system/tests/modules/theme_suggestions_test/src/Hook/ThemeSuggestionsTestHooks.php
+++ b/core/modules/system/tests/modules/theme_suggestions_test/src/Hook/ThemeSuggestionsTestHooks.php
@@ -16,7 +16,7 @@ class ThemeSuggestionsTestHooks {
*/
#[Hook('theme_suggestions_alter')]
public function themeSuggestionsAlter(array &$suggestions, array &$variables, $hook): void {
- \Drupal::messenger()->addStatus('theme_suggestions_test_theme_suggestions_alter' . '() executed.');
+ \Drupal::messenger()->addStatus('theme_suggestions_test_theme_suggestions_alter() executed.');
if ($hook == 'theme_test_general_suggestions') {
$suggestions[] = $hook . '__module_override';
$variables['module_hook'] = 'theme_suggestions_test_theme_suggestions_alter';
@@ -28,7 +28,7 @@ class ThemeSuggestionsTestHooks {
*/
#[Hook('theme_suggestions_theme_test_suggestions_alter')]
public function themeSuggestionsThemeTestSuggestionsAlter(array &$suggestions, array $variables): void {
- \Drupal::messenger()->addStatus('theme_suggestions_test_theme_suggestions_theme_test_suggestions_alter' . '() executed.');
+ \Drupal::messenger()->addStatus('theme_suggestions_test_theme_suggestions_theme_test_suggestions_alter() executed.');
$suggestions[] = 'theme_test_suggestions__module_override';
}
diff --git a/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php b/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php
index 00b51bc72b5..1d953ff8c33 100644
--- a/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php
+++ b/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php
@@ -19,10 +19,11 @@ class ThemeTestSubscriber implements EventSubscriberInterface {
/**
* The used container.
*
+ * @var object
+ *
* @todo This variable is never initialized, so we don't know what it is.
* See https://www.drupal.org/node/2721315
*/
- // phpcs:ignore Drupal.Commenting.VariableComment.Missing, Drupal.Commenting.VariableComment.MissingVar
protected $container;
/**
@@ -78,27 +79,10 @@ class ThemeTestSubscriber implements EventSubscriberInterface {
}
/**
- * Ensures that the theme registry was not initialized.
- */
- public function onView(RequestEvent $event) {
- $current_route = $this->currentRouteMatch->getRouteName();
- $entity_autocomplete_route = [
- 'system.entity_autocomplete',
- ];
-
- if (in_array($current_route, $entity_autocomplete_route)) {
- if ($this->container->initialized('theme.registry')) {
- throw new \Exception('registry initialized');
- }
- }
- }
-
- /**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
$events[KernelEvents::REQUEST][] = ['onRequest'];
- $events[KernelEvents::VIEW][] = ['onView', -1000];
return $events;
}
diff --git a/core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestHooks.php b/core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestHooks.php
index 8e27524449f..9b3fe6cd66a 100644
--- a/core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestHooks.php
+++ b/core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestHooks.php
@@ -85,7 +85,7 @@ class ThemeTestHooks {
*/
#[Hook('theme_suggestions_alter')]
public function themeSuggestionsAlter(array &$suggestions, array $variables, $hook): void {
- \Drupal::messenger()->addStatus('theme_test_theme_suggestions_alter' . '() executed for ' . $hook . '.');
+ \Drupal::messenger()->addStatus('theme_test_theme_suggestions_alter() executed for ' . $hook . '.');
}
/**
@@ -93,7 +93,7 @@ class ThemeTestHooks {
*/
#[Hook('theme_suggestions_theme_test_suggestions_alter')]
public function themeSuggestionsThemeTestSuggestionsAlter(array &$suggestions, array $variables): void {
- \Drupal::messenger()->addStatus('theme_test_theme_suggestions_theme_test_suggestions_alter' . '() executed.');
+ \Drupal::messenger()->addStatus('theme_test_theme_suggestions_theme_test_suggestions_alter() executed.');
}
/**
diff --git a/core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestThemeHooks.php b/core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestThemeHooks.php
index fc48756de51..7bfc10ef0ef 100644
--- a/core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestThemeHooks.php
+++ b/core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestThemeHooks.php
@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Drupal\theme_test\Hook;
-use Drupal\Core\Hook\Attribute\Preprocess;
+use Drupal\Core\Hook\Attribute\Hook;
/**
* Hook implementations for theme_test.
@@ -14,7 +14,7 @@ class ThemeTestThemeHooks {
/**
* Implements hook_preprocess_HOOK().
*/
- #[Preprocess('theme_test_preprocess_suggestions__monkey')]
+ #[Hook('preprocess_theme_test_preprocess_suggestions__monkey')]
public function preprocessTestSuggestions(&$variables): void {
$variables['foo'] = 'Monkey';
}
diff --git a/core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestThemeHooks1.php b/core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestThemeHooks1.php
new file mode 100644
index 00000000000..ddd0e7efc39
--- /dev/null
+++ b/core/modules/system/tests/modules/theme_test/src/Hook/ThemeTestThemeHooks1.php
@@ -0,0 +1,62 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\theme_test\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for theme_test.
+ */
+class ThemeTestThemeHooks1 {
+
+ /**
+ * Implements hook_preprocess_HOOK() for HTML document templates.
+ */
+ #[Hook('preprocess_html')]
+ public function preprocessHtml(&$variables): void {
+ $variables['html_attributes']['theme_test_html_attribute'] = 'theme test html attribute value';
+ $variables['attributes']['theme_test_body_attribute'] = 'theme test body attribute value';
+ $variables['attributes']['theme_test_page_variable'] = 'Page variable is an array.';
+ }
+
+ /**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+ #[Hook('theme_suggestions_theme_test_preprocess_suggestions')]
+ public function themeSuggestionsThemeTestPreprocessSuggestions($variables): array {
+ return [
+ 'theme_test_preprocess_suggestions__' . $variables['foo'],
+ ];
+ }
+
+ /**
+ * Implements hook_preprocess_HOOK().
+ */
+ #[Hook('preprocess_theme_test_preprocess_suggestions')]
+ public function preprocessThemeTestPreprocessSuggestions(&$variables): void {
+ $variables['foo'] = 'Theme hook implementor=theme_theme_test_preprocess_suggestions().';
+ }
+
+ /**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+ #[Hook('theme_suggestions_theme_test_suggestion_provided')]
+ public function themeSuggestionsThemeTestSuggestionProvided(array $variables): array {
+ return [
+ 'theme_test_suggestion_provided__foo',
+ ];
+ }
+
+ /**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+ #[Hook('theme_suggestions_node')]
+ public function themeSuggestionsNode(array $variables): array {
+ $xss = '<script type="text/javascript">alert(\'yo\');</script>';
+ $suggestions[] = 'node__' . $xss;
+ return $suggestions;
+ }
+
+}
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module
index 72c92352b61..b987fb0d40f 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.module
+++ b/core/modules/system/tests/modules/theme_test/theme_test.module
@@ -8,30 +8,6 @@
declare(strict_types=1);
/**
- * Implements hook_preprocess_HOOK() for HTML document templates.
- */
-function theme_test_preprocess_html(&$variables): void {
- $variables['html_attributes']['theme_test_html_attribute'] = 'theme test html attribute value';
- $variables['attributes']['theme_test_body_attribute'] = 'theme test body attribute value';
-
- $variables['attributes']['theme_test_page_variable'] = 'Page variable is an array.';
-}
-
-/**
- * Implements hook_theme_suggestions_HOOK().
- */
-function theme_test_theme_suggestions_theme_test_preprocess_suggestions($variables): array {
- return ['theme_test_preprocess_suggestions__' . $variables['foo']];
-}
-
-/**
- * Implements hook_preprocess_HOOK().
- */
-function theme_test_preprocess_theme_test_preprocess_suggestions(&$variables): void {
- $variables['foo'] = 'Theme hook implementor=theme_theme_test_preprocess_suggestions().';
-}
-
-/**
* Prepares variables for test render element templates.
*
* Default template: theme-test-render-element.html.twig.
@@ -45,23 +21,6 @@ function template_preprocess_theme_test_render_element(&$variables): void {
}
/**
- * Implements hook_theme_suggestions_HOOK().
- */
-function theme_test_theme_suggestions_theme_test_suggestion_provided(array $variables): array {
- return ['theme_test_suggestion_provided__foo'];
-}
-
-/**
- * Implements hook_theme_suggestions_HOOK().
- */
-function theme_test_theme_suggestions_node(array $variables): array {
- $xss = '<script type="text/javascript">alert(\'yo\');</script>';
- $suggestions[] = 'node__' . $xss;
-
- return $suggestions;
-}
-
-/**
* Implements template_preprocess_HOOK() for theme_test_registered_by_module.
*/
function template_preprocess_theme_test_registered_by_module(): void {
diff --git a/core/modules/system/tests/modules/twig_loader_test/src/Loader/TestLoader.php b/core/modules/system/tests/modules/twig_loader_test/src/Loader/TestLoader.php
index 272ad65eff3..f5d0c150118 100644
--- a/core/modules/system/tests/modules/twig_loader_test/src/Loader/TestLoader.php
+++ b/core/modules/system/tests/modules/twig_loader_test/src/Loader/TestLoader.php
@@ -24,7 +24,7 @@ class TestLoader implements LoaderInterface {
/**
* {@inheritdoc}
*/
- public function exists(string $name) {
+ public function exists(string $name): bool {
return TRUE;
}
diff --git a/core/modules/system/tests/modules/update_script_test/src/Hook/UpdateScriptTestRequirements.php b/core/modules/system/tests/modules/update_script_test/src/Hook/UpdateScriptTestRequirements.php
index 5927e31e460..e93fe8bb80f 100644
--- a/core/modules/system/tests/modules/update_script_test/src/Hook/UpdateScriptTestRequirements.php
+++ b/core/modules/system/tests/modules/update_script_test/src/Hook/UpdateScriptTestRequirements.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Drupal\update_script_test\Hook;
use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\Hook\Attribute\Hook;
/**
@@ -25,21 +26,21 @@ class UpdateScriptTestRequirements {
// Set a requirements warning or error when the test requests it.
$requirement_type = $this->configFactory->get('update_script_test.settings')->get('requirement_type');
switch ($requirement_type) {
- case REQUIREMENT_WARNING:
+ case RequirementSeverity::Warning->value:
$requirements['update_script_test'] = [
'title' => 'Update script test',
'value' => 'Warning',
'description' => 'This is a requirements warning provided by the update_script_test module.',
- 'severity' => REQUIREMENT_WARNING,
+ 'severity' => RequirementSeverity::Warning,
];
break;
- case REQUIREMENT_ERROR:
+ case RequirementSeverity::Error->value:
$requirements['update_script_test'] = [
'title' => 'Update script test',
'value' => 'Error',
'description' => 'This is a (buggy description fixed in update_script_test_requirements_alter()) requirements error provided by the update_script_test module.',
- 'severity' => REQUIREMENT_ERROR,
+ 'severity' => RequirementSeverity::Error,
];
break;
}
@@ -51,7 +52,7 @@ class UpdateScriptTestRequirements {
*/
#[Hook('update_requirements_alter')]
public function updateAlter(array &$requirements): void {
- if (isset($requirements['update_script_test']) && $requirements['update_script_test']['severity'] === REQUIREMENT_ERROR) {
+ if (isset($requirements['update_script_test']) && $requirements['update_script_test']['severity'] === RequirementSeverity::Error) {
$requirements['update_script_test']['description'] = 'This is a requirements error provided by the update_script_test module.';
}
}
diff --git a/core/modules/system/tests/modules/update_test_schema/src/Hook/UpdateTestSchemaRequirements.php b/core/modules/system/tests/modules/update_test_schema/src/Hook/UpdateTestSchemaRequirements.php
index de96ce3e36a..3199527bd05 100644
--- a/core/modules/system/tests/modules/update_test_schema/src/Hook/UpdateTestSchemaRequirements.php
+++ b/core/modules/system/tests/modules/update_test_schema/src/Hook/UpdateTestSchemaRequirements.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Drupal\update_test_schema\Hook;
use Drupal\Component\Render\FormattableMarkup;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\Url;
@@ -22,7 +23,7 @@ class UpdateTestSchemaRequirements {
$requirements['path_alias_test'] = [
'title' => 'Path alias test',
'value' => 'Check a path alias for the admin page',
- 'severity' => REQUIREMENT_INFO,
+ 'severity' => RequirementSeverity::Info,
'description' => new FormattableMarkup('Visit <a href=":link">the structure page</a> to do many useful things.', [
':link' => Url::fromRoute('system.admin_structure')->toString(),
]),
diff --git a/core/modules/system/tests/src/Functional/Database/SelectTableSortDefaultTest.php b/core/modules/system/tests/src/Functional/Database/SelectTableSortDefaultTest.php
index 60b7c12b5a1..72e95946a5d 100644
--- a/core/modules/system/tests/src/Functional/Database/SelectTableSortDefaultTest.php
+++ b/core/modules/system/tests/src/Functional/Database/SelectTableSortDefaultTest.php
@@ -28,7 +28,7 @@ class SelectTableSortDefaultTest extends DatabaseTestBase {
['field' => 'Task ID', 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'],
['field' => 'Task', 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'],
['field' => 'Task', 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'],
- // More elements here
+ // More elements here.
];
@@ -56,7 +56,7 @@ class SelectTableSortDefaultTest extends DatabaseTestBase {
['field' => 'Task ID', 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'],
['field' => 'Task', 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'],
['field' => 'Task', 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'],
- // More elements here
+ // More elements here.
];
diff --git a/core/modules/system/tests/src/Functional/Datetime/DrupalDateTimeTest.php b/core/modules/system/tests/src/Functional/Datetime/DrupalDateTimeTest.php
deleted file mode 100644
index cbef10d726e..00000000000
--- a/core/modules/system/tests/src/Functional/Datetime/DrupalDateTimeTest.php
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Drupal\Tests\system\Functional\Datetime;
-
-use Drupal\Core\Datetime\DrupalDateTime;
-use Drupal\Tests\BrowserTestBase;
-use Drupal\user\Entity\User;
-
-/**
- * Tests DrupalDateTime functionality.
- *
- * @group Datetime
- */
-class DrupalDateTimeTest extends BrowserTestBase {
-
- /**
- * Set up required modules.
- *
- * @var string[]
- */
- protected static $modules = [];
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'stark';
-
- /**
- * Tests that DrupalDateTime can detect the right timezone to use.
- *
- * Test with a variety of less commonly used timezone names to
- * help ensure that the system timezone will be different than the
- * stated timezones.
- */
- public function testDateTimezone(): void {
- $date_string = '2007-01-31 21:00:00';
-
- // Make sure no site timezone has been set.
- $this->config('system.date')
- ->set('timezone.user.configurable', 0)
- ->set('timezone.default', NULL)
- ->save();
-
- // Detect the system timezone.
- $system_timezone = date_default_timezone_get();
-
- // Create a date object with an unspecified timezone, which should
- // end up using the system timezone.
- $date = new DrupalDateTime($date_string);
- $timezone = $date->getTimezone()->getName();
- $this->assertSame($system_timezone, $timezone, 'DrupalDateTime uses the system timezone when there is no site timezone.');
-
- // Create a date object with a specified timezone.
- $date = new DrupalDateTime($date_string, 'America/Yellowknife');
- $timezone = $date->getTimezone()->getName();
- $this->assertSame('America/Yellowknife', $timezone, 'DrupalDateTime uses the specified timezone if provided.');
-
- // Set a site timezone.
- $this->config('system.date')->set('timezone.default', 'Europe/Warsaw')->save();
-
- // Create a date object with an unspecified timezone, which should
- // end up using the site timezone.
- $date = new DrupalDateTime($date_string);
- $timezone = $date->getTimezone()->getName();
- $this->assertSame('Europe/Warsaw', $timezone, 'DrupalDateTime uses the site timezone if provided.');
-
- // Create user.
- $this->config('system.date')->set('timezone.user.configurable', 1)->save();
- $test_user = $this->drupalCreateUser([]);
- $this->drupalLogin($test_user);
-
- // Set up the user with a different timezone than the site.
- $edit = ['mail' => $test_user->getEmail(), 'timezone' => 'Asia/Manila'];
- $this->drupalGet('user/' . $test_user->id() . '/edit');
- $this->submitForm($edit, 'Save');
-
- // Reload the user and reset the timezone in AccountProxy::setAccount().
- \Drupal::entityTypeManager()->getStorage('user')->resetCache();
- $this->container->get('current_user')->setAccount(User::load($test_user->id()));
-
- // Create a date object with an unspecified timezone, which should
- // end up using the user timezone.
- $date = new DrupalDateTime($date_string);
- $timezone = $date->getTimezone()->getName();
- $this->assertSame('Asia/Manila', $timezone, 'DrupalDateTime uses the user timezone, if configurable timezones are used and it is set.');
- }
-
- /**
- * Tests the ability to override the time zone in the format method.
- */
- public function testTimezoneFormat(): void {
- // Create a date in UTC
- $date = DrupalDateTime::createFromTimestamp(87654321, 'UTC');
-
- // Verify that the date format method displays the default time zone.
- $this->assertEquals('1972/10/11 12:25:21 UTC', $date->format('Y/m/d H:i:s e'), 'Date has default UTC time zone and correct date/time.');
-
- // Verify that the format method can override the time zone.
- $this->assertEquals('1972/10/11 08:25:21 America/New_York', $date->format('Y/m/d H:i:s e', ['timezone' => 'America/New_York']), 'Date displayed overridden time zone and correct date/time');
-
- // Verify that the date format method still displays the default time zone
- // for the date object.
- $this->assertEquals('1972/10/11 12:25:21 UTC', $date->format('Y/m/d H:i:s e'), 'Date still has default UTC time zone and correct date/time');
- }
-
-}
diff --git a/core/modules/system/tests/src/Functional/Entity/EntityAddUITest.php b/core/modules/system/tests/src/Functional/Entity/EntityAddUITest.php
index 77eaa48575b..f570d803176 100644
--- a/core/modules/system/tests/src/Functional/Entity/EntityAddUITest.php
+++ b/core/modules/system/tests/src/Functional/Entity/EntityAddUITest.php
@@ -62,20 +62,23 @@ class EntityAddUITest extends BrowserTestBase {
$this->drupalGet('/entity_test_with_bundle/add');
$this->assertSession()->addressEquals('/entity_test_with_bundle/add/test');
- // Two bundles exist, confirm both are shown.
+ // Two bundles exist. Confirm both are shown and that they are ordered
+ // alphabetically by their labels, not by their IDs.
EntityTestBundle::create([
'id' => 'test2',
- 'label' => 'Test2 label',
+ 'label' => 'Aaa Test2 label',
'description' => 'My test2 description',
])->save();
$this->drupalGet('/entity_test_with_bundle/add');
$this->assertSession()->linkExists('Test label');
- $this->assertSession()->linkExists('Test2 label');
+ $this->assertSession()->linkExists('Aaa Test2 label');
$this->assertSession()->pageTextContains('My test description');
$this->assertSession()->pageTextContains('My test2 description');
- $this->clickLink('Test2 label');
+ $this->assertSession()->pageTextMatches('/Aaa Test2 label(.*)Test label/');
+
+ $this->clickLink('Aaa Test2 label');
$this->drupalGet('/entity_test_with_bundle/add/test2');
$this->submitForm(['name[0][value]' => 'test name'], 'Save');
@@ -106,7 +109,7 @@ class EntityAddUITest extends BrowserTestBase {
$this->drupalGet('/entity_test_with_bundle/add');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->linkExists('Test label');
- $this->assertSession()->linkExists('Test2 label');
+ $this->assertSession()->linkExists('Aaa Test2 label');
$this->assertSession()->linkNotExists('Forbidden to create bundle');
$this->assertSession()->linkNotExists('Test3 label');
$this->clickLink('Test label');
@@ -129,7 +132,7 @@ class EntityAddUITest extends BrowserTestBase {
$this->drupalGet('/entity_test_with_bundle/add');
$this->assertSession()->linkNotExists('Forbidden to create bundle');
$this->assertSession()->linkNotExists('Test label');
- $this->assertSession()->linkNotExists('Test2 label');
+ $this->assertSession()->linkNotExists('Aaa Test2 label');
$this->assertSession()->linkNotExists('Test3 label');
$this->assertSession()->linkExists('Add a new test entity bundle.');
}
diff --git a/core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php b/core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php
index e130b89bf32..94221d8165f 100644
--- a/core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php
+++ b/core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php
@@ -24,7 +24,7 @@ trait EntityDefinitionTestTrait {
* (optional) Applies changes only for the specified entity type ID.
* Defaults to NULL.
*/
- protected function applyEntityUpdates($entity_type_id = NULL) {
+ protected function applyEntityUpdates($entity_type_id = NULL): void {
$complete_change_list = \Drupal::entityDefinitionUpdateManager()->getChangeList();
if ($complete_change_list) {
// In case there are changes, explicitly invalidate caches.
@@ -68,7 +68,7 @@ trait EntityDefinitionTestTrait {
* @param string $entity_type_id
* The entity type ID.
*/
- protected function doEntityUpdate($op, $entity_type_id) {
+ protected function doEntityUpdate($op, $entity_type_id): void {
$entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
$field_storage_definitions = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions($entity_type_id);
switch ($op) {
@@ -96,7 +96,7 @@ trait EntityDefinitionTestTrait {
* @param array|null $original_storage_definition
* The original field storage definition.
*/
- protected function doFieldUpdate($op, $storage_definition = NULL, $original_storage_definition = NULL) {
+ protected function doFieldUpdate($op, $storage_definition = NULL, $original_storage_definition = NULL): void {
switch ($op) {
case EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED:
\Drupal::service('field_storage_definition.listener')->onFieldStorageDefinitionCreate($storage_definition);
@@ -115,7 +115,7 @@ trait EntityDefinitionTestTrait {
/**
* Enables a new entity type definition.
*/
- protected function enableNewEntityType() {
+ protected function enableNewEntityType(): void {
$this->state->set('entity_test_new', TRUE);
$this->applyEntityUpdates('entity_test_new');
}
@@ -123,7 +123,7 @@ trait EntityDefinitionTestTrait {
/**
* Resets the entity type definition.
*/
- protected function resetEntityType() {
+ protected function resetEntityType(): void {
$updated_entity_type = $this->getUpdatedEntityTypeDefinition(FALSE, FALSE);
$updated_field_storage_definitions = $this->getUpdatedFieldStorageDefinitions(FALSE, FALSE);
$this->entityDefinitionUpdateManager->updateFieldableEntityType($updated_entity_type, $updated_field_storage_definitions);
@@ -136,7 +136,7 @@ trait EntityDefinitionTestTrait {
* (optional) Whether the change should be performed by the entity
* definition update manager.
*/
- protected function updateEntityTypeToRevisionable($perform_update = FALSE) {
+ protected function updateEntityTypeToRevisionable($perform_update = FALSE): void {
$translatable = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update')->isTranslatable();
$updated_entity_type = $this->getUpdatedEntityTypeDefinition(TRUE, $translatable);
@@ -154,7 +154,7 @@ trait EntityDefinitionTestTrait {
* (optional) Whether the change should be performed by the entity
* definition update manager.
*/
- protected function updateEntityTypeToNotRevisionable($perform_update = FALSE) {
+ protected function updateEntityTypeToNotRevisionable($perform_update = FALSE): void {
$translatable = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update')->isTranslatable();
$updated_entity_type = $this->getUpdatedEntityTypeDefinition(FALSE, $translatable);
@@ -172,7 +172,7 @@ trait EntityDefinitionTestTrait {
* (optional) Whether the change should be performed by the entity
* definition update manager.
*/
- protected function updateEntityTypeToTranslatable($perform_update = FALSE) {
+ protected function updateEntityTypeToTranslatable($perform_update = FALSE): void {
$revisionable = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update')->isRevisionable();
$updated_entity_type = $this->getUpdatedEntityTypeDefinition($revisionable, TRUE);
@@ -190,7 +190,7 @@ trait EntityDefinitionTestTrait {
* (optional) Whether the change should be performed by the entity
* definition update manager.
*/
- protected function updateEntityTypeToNotTranslatable($perform_update = FALSE) {
+ protected function updateEntityTypeToNotTranslatable($perform_update = FALSE): void {
$revisionable = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update')->isRevisionable();
$updated_entity_type = $this->getUpdatedEntityTypeDefinition($revisionable, FALSE);
@@ -208,7 +208,7 @@ trait EntityDefinitionTestTrait {
* (optional) Whether the change should be performed by the entity
* definition update manager.
*/
- protected function updateEntityTypeToRevisionableAndTranslatable($perform_update = FALSE) {
+ protected function updateEntityTypeToRevisionableAndTranslatable($perform_update = FALSE): void {
$updated_entity_type = $this->getUpdatedEntityTypeDefinition(TRUE, TRUE);
$updated_field_storage_definitions = $this->getUpdatedFieldStorageDefinitions(TRUE, TRUE);
@@ -235,7 +235,7 @@ trait EntityDefinitionTestTrait {
* (optional) If the base field should be translatable or not. Defaults to
* FALSE.
*/
- protected function addBaseField($type = 'string', $entity_type_id = 'entity_test_update', $is_revisionable = FALSE, $set_label = TRUE, $is_translatable = FALSE) {
+ protected function addBaseField($type = 'string', $entity_type_id = 'entity_test_update', $is_revisionable = FALSE, $set_label = TRUE, $is_translatable = FALSE): void {
$definitions['new_base_field'] = BaseFieldDefinition::create($type)
->setName('new_base_field')
->setRevisionable($is_revisionable)
@@ -251,7 +251,7 @@ trait EntityDefinitionTestTrait {
/**
* Adds a long-named base field to the 'entity_test_update' entity type.
*/
- protected function addLongNameBaseField() {
+ protected function addLongNameBaseField(): void {
$key = 'entity_test_update.additional_base_field_definitions';
$definitions = $this->state->get($key, []);
$definitions['new_long_named_entity_reference_base_field'] = BaseFieldDefinition::create('entity_reference')
@@ -268,7 +268,7 @@ trait EntityDefinitionTestTrait {
* @param string $type
* (optional) The field type for the new field. Defaults to 'string'.
*/
- protected function addRevisionableBaseField($type = 'string') {
+ protected function addRevisionableBaseField($type = 'string'): void {
$definitions['new_base_field'] = BaseFieldDefinition::create($type)
->setName('new_base_field')
->setLabel(t('A new revisionable base field'))
@@ -279,14 +279,14 @@ trait EntityDefinitionTestTrait {
/**
* Modifies the new base field from 'string' to 'text'.
*/
- protected function modifyBaseField() {
+ protected function modifyBaseField(): void {
$this->addBaseField('text');
}
/**
* Promotes a field to an entity key.
*/
- protected function makeBaseFieldEntityKey() {
+ protected function makeBaseFieldEntityKey(): void {
$entity_type = clone \Drupal::entityTypeManager()->getDefinition('entity_test_update');
$entity_keys = $entity_type->getKeys();
$entity_keys['new_base_field'] = 'new_base_field';
@@ -300,21 +300,21 @@ trait EntityDefinitionTestTrait {
* @param string $entity_type_id
* (optional) The entity type ID the base field should be attached to.
*/
- protected function removeBaseField($entity_type_id = 'entity_test_update') {
+ protected function removeBaseField($entity_type_id = 'entity_test_update'): void {
$this->state->delete($entity_type_id . '.additional_base_field_definitions');
}
/**
* Adds a single-field index to the base field.
*/
- protected function addBaseFieldIndex() {
+ protected function addBaseFieldIndex(): void {
$this->state->set('entity_test_update.additional_field_index.entity_test_update.new_base_field', TRUE);
}
/**
* Removes the index added in addBaseFieldIndex().
*/
- protected function removeBaseFieldIndex() {
+ protected function removeBaseFieldIndex(): void {
$this->state->delete('entity_test_update.additional_field_index.entity_test_update.new_base_field');
}
@@ -328,7 +328,7 @@ trait EntityDefinitionTestTrait {
* @param bool $translatable
* (optional) Whether the field should be translatable. Defaults to FALSE.
*/
- protected function addBundleField($type = 'string', $revisionable = FALSE, $translatable = FALSE) {
+ protected function addBundleField($type = 'string', $revisionable = FALSE, $translatable = FALSE): void {
$definitions['new_bundle_field'] = FieldStorageDefinition::create($type)
->setName('new_bundle_field')
->setLabel(t('A new bundle field'))
@@ -342,14 +342,14 @@ trait EntityDefinitionTestTrait {
/**
* Modifies the new bundle field from 'string' to 'text'.
*/
- protected function modifyBundleField() {
+ protected function modifyBundleField(): void {
$this->addBundleField('text');
}
/**
* Removes the new bundle field from the 'entity_test_update' entity type.
*/
- protected function removeBundleField() {
+ protected function removeBundleField(): void {
$this->state->delete('entity_test_update.additional_field_storage_definitions');
$this->state->delete('entity_test_update.additional_bundle_field_definitions.test_bundle');
}
@@ -359,7 +359,7 @@ trait EntityDefinitionTestTrait {
*
* @see \Drupal\entity_test\EntityTestStorageSchema::getEntitySchema()
*/
- protected function addEntityIndex() {
+ protected function addEntityIndex(): void {
$indexes = [
'entity_test_update__new_index' => ['name', 'test_single_property'],
];
@@ -369,14 +369,14 @@ trait EntityDefinitionTestTrait {
/**
* Removes the index added in addEntityIndex().
*/
- protected function removeEntityIndex() {
+ protected function removeEntityIndex(): void {
$this->state->delete('entity_test_update.additional_entity_indexes');
}
/**
* Renames the base table to 'entity_test_update_new'.
*/
- protected function renameBaseTable() {
+ protected function renameBaseTable(): void {
$entity_type = clone \Drupal::entityTypeManager()->getDefinition('entity_test_update');
$entity_type->set('base_table', 'entity_test_update_new');
@@ -387,7 +387,7 @@ trait EntityDefinitionTestTrait {
/**
* Renames the data table to 'entity_test_update_data_new'.
*/
- protected function renameDataTable() {
+ protected function renameDataTable(): void {
$entity_type = clone \Drupal::entityTypeManager()->getDefinition('entity_test_update');
$entity_type->set('data_table', 'entity_test_update_data_new');
@@ -398,7 +398,7 @@ trait EntityDefinitionTestTrait {
/**
* Renames the revision table to 'entity_test_update_revision_new'.
*/
- protected function renameRevisionBaseTable() {
+ protected function renameRevisionBaseTable(): void {
$entity_type = clone \Drupal::entityTypeManager()->getDefinition('entity_test_update');
$entity_type->set('revision_table', 'entity_test_update_revision_new');
@@ -409,7 +409,7 @@ trait EntityDefinitionTestTrait {
/**
* Renames the revision data table to 'entity_test_update_revision_data_new'.
*/
- protected function renameRevisionDataTable() {
+ protected function renameRevisionDataTable(): void {
$entity_type = clone \Drupal::entityTypeManager()->getDefinition('entity_test_update');
$entity_type->set('revision_data_table', 'entity_test_update_revision_data_new');
@@ -420,7 +420,7 @@ trait EntityDefinitionTestTrait {
/**
* Removes the entity type.
*/
- protected function deleteEntityType() {
+ protected function deleteEntityType(): void {
$this->state->set('entity_test_update.entity_type', 'null');
}
diff --git a/core/modules/system/tests/src/Functional/FileTransfer/FileTransferTest.php b/core/modules/system/tests/src/Functional/FileTransfer/FileTransferTest.php
index 2a088655019..61ea15323c1 100644
--- a/core/modules/system/tests/src/Functional/FileTransfer/FileTransferTest.php
+++ b/core/modules/system/tests/src/Functional/FileTransfer/FileTransferTest.php
@@ -57,10 +57,7 @@ class FileTransferTest extends BrowserTestBase {
public function _buildFakeModule() {
$location = 'temporary://fake';
if (is_dir($location)) {
- $ret = 0;
- $output = [];
- exec('rm -Rf ' . escapeshellarg($location), $output, $ret);
- if ($ret != 0) {
+ if (!\Drupal::service('file_system')->deleteRecursive($location)) {
throw new \Exception('Error removing fake module directory.');
}
}
@@ -80,7 +77,7 @@ class FileTransferTest extends BrowserTestBase {
$this->_writeDirectory($base . DIRECTORY_SEPARATOR . $key, $file);
}
else {
- // Just write the filename into the file
+ // Just write the filename into the file.
file_put_contents($base . DIRECTORY_SEPARATOR . $file, $file);
}
}
@@ -91,26 +88,10 @@ class FileTransferTest extends BrowserTestBase {
*/
public function testJail(): void {
$source = $this->_buildFakeModule();
-
- // This convoluted piece of code is here because our testing framework does
- // not support expecting exceptions.
- $got_it = FALSE;
- try {
- $this->testConnection->copyDirectory($source, sys_get_temp_dir());
- }
- catch (FileTransferException) {
- $got_it = TRUE;
- }
- $this->assertTrue($got_it, 'Was not able to copy a directory outside of the jailed area.');
-
- $got_it = TRUE;
- try {
- $this->testConnection->copyDirectory($source, $this->root . '/' . PublicStream::basePath());
- }
- catch (FileTransferException) {
- $got_it = FALSE;
- }
- $this->assertTrue($got_it, 'Was able to copy a directory inside of the jailed area');
+ $this->testConnection->copyDirectory($source, $this->root . '/' . PublicStream::basePath());
+ $this->expectException(FileTransferException::class);
+ $this->expectExceptionMessage('@directory is outside of the @jail');
+ $this->testConnection->copyDirectory($source, sys_get_temp_dir());
}
}
diff --git a/core/modules/system/tests/src/Functional/Form/ElementTest.php b/core/modules/system/tests/src/Functional/Form/ElementTest.php
index 4a9755fac7f..294c4e2ec34 100644
--- a/core/modules/system/tests/src/Functional/Form/ElementTest.php
+++ b/core/modules/system/tests/src/Functional/Form/ElementTest.php
@@ -38,6 +38,7 @@ class ElementTest extends BrowserTestBase {
$this->testFormAutocomplete();
$this->testFormElementErrors();
$this->testDetailsSummaryAttributes();
+ $this->testDetailsDescriptionAttributes();
}
/**
@@ -153,10 +154,10 @@ class ElementTest extends BrowserTestBase {
* Tests the submit_button attribute.
*/
protected function testSubmitButtonAttribute(): void {
- // Set the submit_button attribute to true
+ // Set the submit_button attribute to true.
$this->drupalGet('form-test/submit-button-attribute');
$this->assertSession()->elementsCount('xpath', '//input[@type="submit"]', 1);
- // Set the submit_button attribute to false
+ // Set the submit_button attribute to false.
$this->drupalGet('form-test/submit-button-attribute/1');
$this->assertSession()->elementsCount('xpath', '//input[@type="button"]', 1);
}
@@ -230,4 +231,13 @@ class ElementTest extends BrowserTestBase {
$this->assertSession()->elementExists('css', 'summary[data-summary-attribute="test"]');
}
+ /**
+ * Tests description attributes of details.
+ */
+ protected function testDetailsDescriptionAttributes(): void {
+ $this->drupalGet('form-test/group-details');
+ $this->assertSession()->elementExists('css', 'details[aria-describedby="edit-description-attributes--description"]');
+ $this->assertSession()->elementExists('css', 'div[id="edit-description-attributes--description"]');
+ }
+
}
diff --git a/core/modules/system/tests/src/Functional/Form/FormTest.php b/core/modules/system/tests/src/Functional/Form/FormTest.php
index 6812903dccc..c7cb67c72c7 100644
--- a/core/modules/system/tests/src/Functional/Form/FormTest.php
+++ b/core/modules/system/tests/src/Functional/Form/FormTest.php
@@ -6,7 +6,6 @@ namespace Drupal\Tests\system\Functional\Form;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
-use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Form\FormState;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
@@ -199,7 +198,7 @@ class FormTest extends BrowserTestBase {
$expected_key = array_search($error->getText(), $expected);
// If the error message is not one of the expected messages, fail.
if ($expected_key === FALSE) {
- $this->fail(new FormattableMarkup("Unexpected error message: @error", ['@error' => $error[0]]));
+ $this->fail("Unexpected error message: " . $error[0]);
}
// Remove the expected message from the list once it is found.
else {
@@ -209,7 +208,7 @@ class FormTest extends BrowserTestBase {
// Fail if any expected messages were not found.
foreach ($expected as $not_found) {
- $this->fail(new FormattableMarkup("Found error message: @error", ['@error' => $not_found]));
+ $this->fail("Found error message: " . $not_found);
}
// Verify that input elements are still empty.
@@ -307,12 +306,12 @@ class FormTest extends BrowserTestBase {
];
$this->submitForm($edit, 'Submit');
// Verify that the error message is displayed with invalid token even when
- // required fields are filled.'
+ // required fields are filled.
$this->assertSession()->elementExists('xpath', '//div[contains(@class, "error")]');
$this->assertSession()->pageTextContains('The form has become outdated.');
$this->assertSession()->fieldValueEquals('integer_step', 5);
- // Check a form with a URL field
+ // Check a form with a URL field.
$this->drupalGet(Url::fromRoute('form_test.url'));
$this->assertSession()
->elementExists('css', 'input[name="form_token"]')
@@ -610,14 +609,6 @@ class FormTest extends BrowserTestBase {
public function testNumber(): void {
$form = \Drupal::formBuilder()->getForm('\Drupal\form_test\Form\FormTestNumberForm');
- // Array with all the error messages to be checked.
- $error_messages = [
- 'no_number' => '%name must be a number.',
- 'too_low' => '%name must be higher than or equal to %min.',
- 'too_high' => '%name must be lower than or equal to %max.',
- 'step_mismatch' => '%name is not a valid number.',
- ];
-
// The expected errors.
$expected = [
'integer_no_number' => 'no_number',
@@ -648,21 +639,26 @@ class FormTest extends BrowserTestBase {
$this->submitForm([], 'Submit');
foreach ($expected as $element => $error) {
- // Create placeholder array.
- $placeholders = [
- '%name' => $form[$element]['#title'],
- '%min' => $form[$element]['#min'] ?? '0',
- '%max' => $form[$element]['#max'] ?? '0',
+ // Array with all the error messages to be checked.
+ $name = $form[$element]['#title'];
+ $min = $form[$element]['#min'] ?? '0';
+ $max = $form[$element]['#max'] ?? '0';
+
+ $error_messages = [
+ 'no_number' => "<em class=\"placeholder\">$name</em> must be a number.",
+ 'too_low' => "<em class=\"placeholder\">$name</em> must be higher than or equal to <em class=\"placeholder\">$min</em>.",
+ 'too_high' => "<em class=\"placeholder\">$name</em> must be lower than or equal to <em class=\"placeholder\">$max</em>.",
+ 'step_mismatch' => "<em class=\"placeholder\">$name</em> is not a valid number.",
];
foreach ($error_messages as $id => $message) {
// Check if the error exists on the page, if the current message ID is
// expected. Otherwise ensure that the error message is not present.
if ($id === $error) {
- $this->assertSession()->responseContains(new FormattableMarkup($message, $placeholders));
+ $this->assertSession()->responseContains($message);
}
else {
- $this->assertSession()->responseNotContains(new FormattableMarkup($message, $placeholders));
+ $this->assertSession()->responseNotContains($message);
}
}
}
diff --git a/core/modules/system/tests/src/Functional/Hook/HookCollectorPassTest.php b/core/modules/system/tests/src/Functional/Hook/HookCollectorPassTest.php
new file mode 100644
index 00000000000..59d69d1242c
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Hook/HookCollectorPassTest.php
@@ -0,0 +1,61 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\system\Functional\Hook;
+
+use Drupal\Tests\BrowserTestBase;
+use Drupal\Core\Url;
+
+/**
+ * Tests services in .module files.
+ *
+ * @group Hook
+ */
+class HookCollectorPassTest extends BrowserTestBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $modules = ['container_initialize'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected $defaultTheme = 'stark';
+
+ /**
+ * Tests installing a module with a Drupal container call outside functions.
+ *
+ * If this is removed then it needs to be moved to a test that installs modules through
+ * admin/modules.
+ */
+ public function testContainerOutsideFunction(): void {
+ $settings['settings']['rebuild_access'] = (object) [
+ 'value' => TRUE,
+ 'required' => TRUE,
+ ];
+
+ // This simulates installing the module and running a cache rebuild in a
+ // separate request.
+ $this->writeSettings($settings);
+ $this->rebuildAll();
+ $this->drupalGet(Url::fromUri('base:core/rebuild.php'));
+ $this->assertSession()->pageTextNotContains('ContainerNotInitializedException');
+ // Successful response from rebuild.php should redirect to the front page.
+ $this->assertSession()->addressEquals('/');
+
+ // If this file is removed then this test needs to be updated to trigger
+ // the container rebuild error from https://www.drupal.org/i/3505049
+ $config_module_file = $this->root . '/core/modules/system/tests/modules/container_initialize/container_initialize.module';
+ $this->assertFileExists($config_module_file, 'This test depends on a container call in a .module file');
+ // Confirm that the file still has a bare container call.
+ $bare_container = "declare(strict_types=1);
+
+\Drupal::getContainer()->getParameter('site.path');
+";
+ $file_content = file_get_contents($config_module_file);
+ $this->assertStringContainsString($bare_container, $file_content, 'container_initialize.module container test feature is missing.');
+ }
+
+}
diff --git a/core/modules/system/tests/src/Functional/Menu/LinksetControllerMultiLingualTest.php b/core/modules/system/tests/src/Functional/Menu/LinksetControllerMultiLingualTest.php
index d6b44d4bb9d..b5e20847466 100644
--- a/core/modules/system/tests/src/Functional/Menu/LinksetControllerMultiLingualTest.php
+++ b/core/modules/system/tests/src/Functional/Menu/LinksetControllerMultiLingualTest.php
@@ -151,7 +151,7 @@ final class LinksetControllerMultiLingualTest extends LinksetControllerTestBase
]);
foreach (['aa', 'bb', 'cc'] as $language_code) {
$multi_lingual_menu_item->addTranslation($language_code, [
- 'title' => $language_code . '|' . 'A multi-lingual-node',
+ 'title' => $language_code . '|A multi-lingual-node',
]);
$multi_lingual_menu_item->save();
}
@@ -170,7 +170,7 @@ final class LinksetControllerMultiLingualTest extends LinksetControllerTestBase
]);
foreach (['aa', 'bb'] as $language_code) {
$multi_lingual_menu_item->addTranslation($language_code, [
- 'title' => $language_code . '|' . 'Second multi-lingual-node',
+ 'title' => $language_code . '|Second multi-lingual-node',
]);
$multi_lingual_menu_item->save();
}
@@ -189,7 +189,7 @@ final class LinksetControllerMultiLingualTest extends LinksetControllerTestBase
]);
foreach (['aa', 'bb'] as $language_code) {
$multi_lingual_menu_item->addTranslation($language_code, [
- 'title' => $language_code . '|' . 'Third multi-lingual-node',
+ 'title' => $language_code . '|Third multi-lingual-node',
]);
$multi_lingual_menu_item->save();
}
diff --git a/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php b/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php
index f2df8a91458..c7d6f6d7d2b 100644
--- a/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php
+++ b/core/modules/system/tests/src/Functional/Menu/LocalTasksTest.php
@@ -170,7 +170,7 @@ class LocalTasksTest extends BrowserTestBase {
$this->assertEquals('Settings', $result[0]->getText(), 'The settings tab is active.');
$this->assertEquals('Derive 1', $result[1]->getText(), 'The derive1 tab is active.');
- // Ensures that the local tasks contains the proper 'provider key'
+ // Ensures that the local tasks contains the proper 'provider key'.
$definitions = $this->container->get('plugin.manager.menu.local_task')->getDefinitions();
$this->assertEquals('menu_test', $definitions['menu_test.local_task_test_tasks_view']['provider']);
$this->assertEquals('menu_test', $definitions['menu_test.local_task_test_tasks_edit']['provider']);
diff --git a/core/modules/system/tests/src/Functional/Module/DependencyTest.php b/core/modules/system/tests/src/Functional/Module/DependencyTest.php
index 7f2d218388a..2a51e4b8de8 100644
--- a/core/modules/system/tests/src/Functional/Module/DependencyTest.php
+++ b/core/modules/system/tests/src/Functional/Module/DependencyTest.php
@@ -142,7 +142,7 @@ class DependencyTest extends ModuleTestBase {
$this->assertSession()->fieldEnabled('modules[system_no_module_version_dependency_test][enable]');
$this->assertSession()->fieldDisabled('modules[system_no_module_version_test][enable]');
- // Remove the version requirement from the dependency definition
+ // Remove the version requirement from the dependency definition.
$info = [
'type' => 'module',
'core_version_requirement' => '*',
@@ -253,9 +253,8 @@ class DependencyTest extends ModuleTestBase {
$this->resetAll();
$this->assertModules(['module_test'], TRUE);
\Drupal::state()->set('module_test.dependency', 'dependency');
- // module_test creates a dependency chain:
- // - dblog depends on config
- // - config depends on help
+ // module_test creates a dependency chain: dblog depends on config which
+ // depends on help.
$expected_order = ['help', 'config', 'dblog'];
// Enable the modules through the UI, verifying that the dependency chain
diff --git a/core/modules/system/tests/src/Functional/Module/GenericModuleTestBase.php b/core/modules/system/tests/src/Functional/Module/GenericModuleTestBase.php
index 7b3754bc34c..d22f433a414 100644
--- a/core/modules/system/tests/src/Functional/Module/GenericModuleTestBase.php
+++ b/core/modules/system/tests/src/Functional/Module/GenericModuleTestBase.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Drupal\Tests\system\Functional\Module;
use Drupal\Core\Database\Database;
+use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Tests\BrowserTestBase;
/**
@@ -50,9 +51,12 @@ abstract class GenericModuleTestBase extends BrowserTestBase {
if (empty($info['required'])) {
$connection = Database::getConnection();
- // When the database driver is provided by a module, then that module
- // cannot be uninstalled.
- if ($module !== $connection->getProvider()) {
+ // The module that provides the database driver, or is a dependency of
+ // the database driver, cannot be uninstalled.
+ $database_module_extension = \Drupal::service(ModuleExtensionList::class)->get($connection->getProvider());
+ $database_modules_required = $database_module_extension->requires ? array_keys($database_module_extension->requires) : [];
+ $database_modules_required[] = $connection->getProvider();
+ if (!in_array($module, $database_modules_required)) {
// Check that the module can be uninstalled and then re-installed again.
$this->preUnInstallSteps();
$this->assertTrue(\Drupal::service('module_installer')->uninstall([$module]), "Failed to uninstall '$module' module");
diff --git a/core/modules/system/tests/src/Functional/Module/UninstallTest.php b/core/modules/system/tests/src/Functional/Module/UninstallTest.php
index aeace5bb488..6d1b93cc50d 100644
--- a/core/modules/system/tests/src/Functional/Module/UninstallTest.php
+++ b/core/modules/system/tests/src/Functional/Module/UninstallTest.php
@@ -22,7 +22,15 @@ class UninstallTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
- protected static $modules = ['module_test', 'user', 'views', 'node'];
+ protected static $modules = [
+ 'ckeditor5',
+ 'filter',
+ 'module_test',
+ 'node',
+ 'user',
+ 'views',
+ 'views_ui',
+ ];
/**
* {@inheritdoc}
@@ -118,6 +126,13 @@ class UninstallTest extends BrowserTestBase {
// Delete the node to allow node to be uninstalled.
$node->delete();
+ // Ensure dependent module full names are shown.
+ $this->assertSession()->pageTextContains('Required by: Views UI');
+ // Ensure matching machine names do not display.
+ $this->assertSession()->pageTextNotContains('Required by: Views UI (views_ui)');
+ // Ensure machine names that do not match do display.
+ $this->assertSession()->pageTextContains('Text Editor (editor)');
+
// Uninstall module_test.
$edit = [];
$edit['uninstall[module_test]'] = TRUE;
diff --git a/core/modules/system/tests/src/Functional/Module/VersionTest.php b/core/modules/system/tests/src/Functional/Module/VersionTest.php
index 7f3af598cfb..1a98b7f246f 100644
--- a/core/modules/system/tests/src/Functional/Module/VersionTest.php
+++ b/core/modules/system/tests/src/Functional/Module/VersionTest.php
@@ -8,6 +8,7 @@ namespace Drupal\Tests\system\Functional\Module;
* Tests module version dependencies.
*
* @group Module
+ * @group #slow
*/
class VersionTest extends ModuleTestBase {
diff --git a/core/modules/system/tests/src/Functional/Pager/PagerTest.php b/core/modules/system/tests/src/Functional/Pager/PagerTest.php
index 9d0cae26b41..3cbde95efb4 100644
--- a/core/modules/system/tests/src/Functional/Pager/PagerTest.php
+++ b/core/modules/system/tests/src/Functional/Pager/PagerTest.php
@@ -179,7 +179,7 @@ class PagerTest extends BrowserTestBase {
// We loop through the page with the test data query parameters, and check
// that the active page for each pager element has the expected page
- // (1-indexed) and resulting query parameter
+ // (1-indexed) and resulting query parameter.
foreach ($test_data as $data) {
$input_query = str_replace(' ', '%20', $data['input_query']);
$this->drupalGet($this->getAbsoluteUrl(parse_url($this->getUrl())['path'] . $input_query), ['external' => TRUE]);
diff --git a/core/modules/system/tests/src/Functional/ParamConverter/UpcastingTest.php b/core/modules/system/tests/src/Functional/ParamConverter/UpcastingTest.php
index a3a89112afe..165fb692952 100644
--- a/core/modules/system/tests/src/Functional/ParamConverter/UpcastingTest.php
+++ b/core/modules/system/tests/src/Functional/ParamConverter/UpcastingTest.php
@@ -41,19 +41,19 @@ class UpcastingTest extends BrowserTestBase {
$user = $this->drupalCreateUser(['access content']);
$foo = 'bar';
- // paramconverter_test/test_user_node_foo/{user}/{node}/{foo}
+ // Test "paramconverter_test/test_user_node_foo/{user}/{node}/{foo}".
$this->drupalGet("paramconverter_test/test_user_node_foo/" . $user->id() . '/' . $node->id() . "/$foo");
// Verify user and node upcast by entity name.
$this->assertSession()->pageTextContains("user: {$user->label()}, node: {$node->label()}, foo: $foo");
- // paramconverter_test/test_node_user_user/{node}/{foo}/{user}
- // options.parameters.foo.type = entity:user
+ // Test "paramconverter_test/test_node_user_user/{node}/{foo}/{user}" with
+ // "options.parameters.foo.type = entity:user".
$this->drupalGet("paramconverter_test/test_node_user_user/" . $node->id() . "/" . $user->id() . "/" . $user->id());
// Verify foo converted to user as well.
$this->assertSession()->pageTextContains("user: {$user->label()}, node: {$node->label()}, foo: {$user->label()}");
- // paramconverter_test/test_node_node_foo/{user}/{node}/{foo}
- // options.parameters.user.type = entity:node
+ // Test "paramconverter_test/test_node_node_foo/{user}/{node}/{foo}" with
+ // "options.parameters.user.type = entity:node".
$this->drupalGet("paramconverter_test/test_node_node_foo/" . $node->id() . "/" . $node->id() . "/$foo");
// Verify that user is upcast to node (rather than to user).
$this->assertSession()->pageTextContains("user: {$node->label()}, node: {$node->label()}, foo: $foo");
@@ -65,8 +65,8 @@ class UpcastingTest extends BrowserTestBase {
public function testSameTypes(): void {
$node = $this->drupalCreateNode(['title' => $this->randomMachineName(8)]);
$parent = $this->drupalCreateNode(['title' => $this->randomMachineName(8)]);
- // paramconverter_test/node/{node}/set/parent/{parent}
- // options.parameters.parent.type = entity:node
+ // Test "paramconverter_test/node/{node}/set/parent/{parent}" with
+ // "options.parameters.parent.type = entity:node".
$this->drupalGet("paramconverter_test/node/" . $node->id() . "/set/parent/" . $parent->id());
$this->assertSession()->pageTextContains("Setting '" . $parent->getTitle() . "' as parent of '" . $node->getTitle() . "'.");
}
diff --git a/core/modules/system/tests/src/Functional/SecurityAdvisories/SecurityAdvisoryTest.php b/core/modules/system/tests/src/Functional/SecurityAdvisories/SecurityAdvisoryTest.php
index 388e83f6fcc..b297647194a 100644
--- a/core/modules/system/tests/src/Functional/SecurityAdvisories/SecurityAdvisoryTest.php
+++ b/core/modules/system/tests/src/Functional/SecurityAdvisories/SecurityAdvisoryTest.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Drupal\Tests\system\Functional\SecurityAdvisories;
use Drupal\advisory_feed_test\AdvisoryTestClientMiddleware;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\Traits\Core\CronRunTrait;
@@ -140,10 +141,10 @@ class SecurityAdvisoryTest extends BrowserTestBase {
// If both PSA and non-PSA advisories are displayed they should be displayed
// as errors.
- $this->assertStatusReportLinks($mixed_advisory_links, REQUIREMENT_ERROR);
+ $this->assertStatusReportLinks($mixed_advisory_links, RequirementSeverity::Error);
// The advisories will be displayed on admin pages if the response was
// stored from the status report request.
- $this->assertAdminPageLinks($mixed_advisory_links, REQUIREMENT_ERROR);
+ $this->assertAdminPageLinks($mixed_advisory_links, RequirementSeverity::Error);
// Confirm that a user without the correct permission will not see the
// advisories on admin pages.
@@ -159,8 +160,8 @@ class SecurityAdvisoryTest extends BrowserTestBase {
$this->drupalLogin($this->user);
// Test cache.
AdvisoryTestClientMiddleware::setTestEndpoint($this->nonWorkingEndpoint);
- $this->assertAdminPageLinks($mixed_advisory_links, REQUIREMENT_ERROR);
- $this->assertStatusReportLinks($mixed_advisory_links, REQUIREMENT_ERROR);
+ $this->assertAdminPageLinks($mixed_advisory_links, RequirementSeverity::Error);
+ $this->assertStatusReportLinks($mixed_advisory_links, RequirementSeverity::Error);
// Tests transmit errors with a JSON endpoint.
$this->tempStore->delete('advisories_response');
@@ -195,8 +196,8 @@ class SecurityAdvisoryTest extends BrowserTestBase {
$this->assertAdvisoriesNotDisplayed($psa_advisory_links, ['system.admin']);
// If only PSA advisories are displayed they should be displayed as
// warnings.
- $this->assertStatusReportLinks($psa_advisory_links, REQUIREMENT_WARNING);
- $this->assertAdminPageLinks($psa_advisory_links, REQUIREMENT_WARNING);
+ $this->assertStatusReportLinks($psa_advisory_links, RequirementSeverity::Warning);
+ $this->assertAdminPageLinks($psa_advisory_links, RequirementSeverity::Warning);
AdvisoryTestClientMiddleware::setTestEndpoint($this->workingEndpointNonPsaOnly, TRUE);
$non_psa_advisory_links = [
@@ -205,8 +206,8 @@ class SecurityAdvisoryTest extends BrowserTestBase {
];
// If only non-PSA advisories are displayed they should be displayed as
// errors.
- $this->assertStatusReportLinks($non_psa_advisory_links, REQUIREMENT_ERROR);
- $this->assertAdminPageLinks($non_psa_advisory_links, REQUIREMENT_ERROR);
+ $this->assertStatusReportLinks($non_psa_advisory_links, RequirementSeverity::Error);
+ $this->assertAdminPageLinks($non_psa_advisory_links, RequirementSeverity::Error);
// Confirm that advisory fetching can be disabled after enabled.
$this->config('system.advisories')->set('enabled', FALSE)->save();
@@ -220,16 +221,15 @@ class SecurityAdvisoryTest extends BrowserTestBase {
*
* @param string[] $expected_link_texts
* The expected links' text.
- * @param int $error_or_warning
- * Whether the links are a warning or an error. Should be one of the
- * REQUIREMENT_* constants.
+ * @param \Drupal\Core\Extension\Requirement\RequirementSeverity $error_or_warning
+ * Whether the links are a warning or an error.
*
* @internal
*/
- private function assertAdminPageLinks(array $expected_link_texts, int $error_or_warning): void {
+ private function assertAdminPageLinks(array $expected_link_texts, RequirementSeverity $error_or_warning): void {
$assert = $this->assertSession();
$this->drupalGet(Url::fromRoute('system.admin'));
- if ($error_or_warning === REQUIREMENT_ERROR) {
+ if ($error_or_warning === RequirementSeverity::Error) {
$assert->pageTextContainsOnce('Error message');
$assert->pageTextNotContains('Warning message');
}
@@ -247,16 +247,15 @@ class SecurityAdvisoryTest extends BrowserTestBase {
*
* @param string[] $expected_link_texts
* The expected links' text.
- * @param int $error_or_warning
- * Whether the links are a warning or an error. Should be one of the
- * REQUIREMENT_* constants.
+ * @param \Drupal\Core\Extension\Requirement\RequirementSeverity::Error|\Drupal\Core\Extension\Requirement\RequirementSeverity::Warning $error_or_warning
+ * Whether the links are a warning or an error.
*
* @internal
*/
- private function assertStatusReportLinks(array $expected_link_texts, int $error_or_warning): void {
+ private function assertStatusReportLinks(array $expected_link_texts, RequirementSeverity $error_or_warning): void {
$this->drupalGet(Url::fromRoute('system.status'));
$assert = $this->assertSession();
- $selector = 'h3#' . ($error_or_warning === REQUIREMENT_ERROR ? 'error' : 'warning')
+ $selector = 'h3#' . $error_or_warning->status()
. ' ~ details.system-status-report__entry:contains("Critical security announcements")';
$assert->elementExists('css', $selector);
foreach ($expected_link_texts as $expected_link_text) {
diff --git a/core/modules/system/tests/src/Functional/Session/LegacySessionTest.php b/core/modules/system/tests/src/Functional/Session/LegacySessionTest.php
new file mode 100644
index 00000000000..84ab1ed9d5b
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Session/LegacySessionTest.php
@@ -0,0 +1,44 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\system\Functional\Session;
+
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Drupal legacy session handling tests.
+ *
+ * @group legacy
+ * @group Session
+ */
+class LegacySessionTest extends BrowserTestBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $modules = ['session_test'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected $defaultTheme = 'stark';
+
+ /**
+ * Tests data persistence via the session_test module callbacks.
+ */
+ public function testLegacyDataPersistence(): void {
+ $this->expectDeprecation('Storing values directly in $_SESSION is deprecated in drupal:11.2.0 and will become unsupported in drupal:12.0.0. Use $request-&gt;getSession()-&gt;set() instead. Affected keys: legacy_test_value. See https://www.drupal.org/node/3518527');
+ $value = $this->randomMachineName();
+
+ // Verify that the session value is stored.
+ $this->drupalGet('session-test/legacy-set/' . $value);
+ $this->assertSession()->pageTextContains($value);
+
+ // Verify that the session correctly returned the stored data for an
+ // authenticated user.
+ $this->drupalGet('session-test/legacy-get');
+ $this->assertSession()->pageTextContains($value);
+ }
+
+}
diff --git a/core/modules/system/tests/src/Functional/System/AccessDeniedTest.php b/core/modules/system/tests/src/Functional/System/AccessDeniedTest.php
index 0163c594a06..51d8d20713e 100644
--- a/core/modules/system/tests/src/Functional/System/AccessDeniedTest.php
+++ b/core/modules/system/tests/src/Functional/System/AccessDeniedTest.php
@@ -115,7 +115,7 @@ class AccessDeniedTest extends BrowserTestBase {
$this->assertSession()->statusCodeEquals(403);
$this->assertSession()->pageTextContains('Username');
- // Log back in, set the custom 403 page to /user/login and remove the block
+ // Log back in, set the custom 403 page to /user/login and remove the block.
$this->drupalLogin($this->adminUser);
$this->config('system.site')->set('page.403', '/user/login')->save();
$block->disable()->save();
diff --git a/core/modules/system/tests/src/Functional/System/PageTitleTest.php b/core/modules/system/tests/src/Functional/System/PageTitleTest.php
index d225842a236..cf2a5a61512 100644
--- a/core/modules/system/tests/src/Functional/System/PageTitleTest.php
+++ b/core/modules/system/tests/src/Functional/System/PageTitleTest.php
@@ -126,7 +126,7 @@ class PageTitleTest extends BrowserTestBase {
$this->assertSession()->titleEquals('Foo | Drupal');
$this->assertSession()->elementTextEquals('xpath', '//h1[@class="page-title"]', 'Foo');
- // Test forms
+ // Test forms.
$this->drupalGet('form-test/object-builder');
$this->assertSession()->titleEquals('Test dynamic title | Drupal');
diff --git a/core/modules/system/tests/src/Functional/System/SitesDirectoryHardeningTest.php b/core/modules/system/tests/src/Functional/System/SitesDirectoryHardeningTest.php
index 41d60b8a42a..32487fa8604 100644
--- a/core/modules/system/tests/src/Functional/System/SitesDirectoryHardeningTest.php
+++ b/core/modules/system/tests/src/Functional/System/SitesDirectoryHardeningTest.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Drupal\Tests\system\Functional\System;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\Site\Settings;
use Drupal\Tests\BrowserTestBase;
@@ -58,7 +59,7 @@ class SitesDirectoryHardeningTest extends BrowserTestBase {
// Manually trigger the requirements check.
$requirements = $this->checkSystemRequirements();
- $this->assertEquals(REQUIREMENT_WARNING, $requirements['configuration_files']['severity'], 'Warning severity is properly set.');
+ $this->assertEquals(RequirementSeverity::Warning, $requirements['configuration_files']['severity'], 'Warning severity is properly set.');
$this->assertEquals('Protection disabled', (string) $requirements['configuration_files']['value']);
$description = strip_tags((string) \Drupal::service('renderer')->renderInIsolation($requirements['configuration_files']['description']));
$this->assertStringContainsString('settings.php is not protected from modifications and poses a security risk.', $description);
@@ -91,8 +92,7 @@ class SitesDirectoryHardeningTest extends BrowserTestBase {
* An array of system requirements.
*/
protected function checkSystemRequirements() {
- \Drupal::moduleHandler()->loadInclude('system', 'install');
- return system_requirements('runtime');
+ return \Drupal::moduleHandler()->invoke('system', 'runtime_requirements');
}
/**
diff --git a/core/modules/system/tests/src/Functional/System/StatusTest.php b/core/modules/system/tests/src/Functional/System/StatusTest.php
index 972ac14fb2c..b5e32759a73 100644
--- a/core/modules/system/tests/src/Functional/System/StatusTest.php
+++ b/core/modules/system/tests/src/Functional/System/StatusTest.php
@@ -97,6 +97,8 @@ class StatusTest extends BrowserTestBase {
$this->drupalGet('admin/reports/status/php');
$this->assertSession()->statusCodeEquals(200);
+ $this->assertSession()->pageTextContains('PHP');
+ $this->assertSession()->pageTextNotContains('$_COOKIE');
$settings['settings']['sa_core_2023_004_phpinfo_flags'] = (object) [
'value' => INFO_ALL,
diff --git a/core/modules/system/tests/src/Functional/Theme/FastTest.php b/core/modules/system/tests/src/Functional/Theme/FastTest.php
deleted file mode 100644
index 5cbb7d8f277..00000000000
--- a/core/modules/system/tests/src/Functional/Theme/FastTest.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Drupal\Tests\system\Functional\Theme;
-
-use Drupal\Tests\BrowserTestBase;
-use Drupal\user\Entity\User;
-
-/**
- * Tests autocompletion not loading registry.
- *
- * @group Theme
- */
-class FastTest extends BrowserTestBase {
-
- /**
- * {@inheritdoc}
- */
- protected static $modules = ['theme_test'];
-
- /**
- * {@inheritdoc}
- */
- protected $defaultTheme = 'stark';
-
- /**
- * User allowed to access use profiles.
- *
- * @var \Drupal\user\Entity\User
- */
- protected User $account;
-
- /**
- * {@inheritdoc}
- */
- protected function setUp(): void {
- parent::setUp();
- $this->account = $this->drupalCreateUser(['access user profiles']);
- }
-
- /**
- * Tests access to user autocompletion and verify the correct results.
- */
- public function testUserAutocomplete(): void {
- $this->drupalLogin($this->account);
- $this->drupalGet('user/autocomplete', ['query' => ['q' => $this->account->getAccountName()]]);
- $this->assertSession()->responseContains($this->account->getAccountName());
- $this->assertSession()->pageTextNotContains('registry initialized');
- }
-
-}
diff --git a/core/modules/system/tests/src/Functional/UpdateSystem/UpdatePathTestJavaScriptTest.php b/core/modules/system/tests/src/Functional/UpdateSystem/UpdatePathTestJavaScriptTest.php
index ffaaba1119a..32ca94f100b 100644
--- a/core/modules/system/tests/src/Functional/UpdateSystem/UpdatePathTestJavaScriptTest.php
+++ b/core/modules/system/tests/src/Functional/UpdateSystem/UpdatePathTestJavaScriptTest.php
@@ -50,7 +50,7 @@ class UpdatePathTestJavaScriptTest extends BrowserTestBase {
}
// Source is a root-relative URL. Transform it to an absolute URL to allow
// file_get_contents() to access the file.
- $src = preg_replace('#^' . $GLOBALS['base_path'] . '(.*)#i', $GLOBALS['base_url'] . '/' . '${1}', $script->getAttribute('src'));
+ $src = preg_replace('#^' . $GLOBALS['base_path'] . '(.*)#i', $GLOBALS['base_url'] . '/${1}', $script->getAttribute('src'));
$file_content = file_get_contents($src);
if (str_contains($file_content, 'window.drupalSettings =')) {
diff --git a/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php b/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php
index f0f78b23c99..5be7e48289f 100644
--- a/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php
+++ b/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Drupal\Tests\system\Functional\UpdateSystem;
use Drupal\Component\Serialization\Yaml;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\Url;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
@@ -149,7 +150,7 @@ class UpdateScriptTest extends BrowserTestBase {
// First, run this test with pending updates to make sure they can be run
// successfully.
$this->drupalLogin($this->updateUser);
- $update_script_test_config->set('requirement_type', REQUIREMENT_WARNING)->save();
+ $update_script_test_config->set('requirement_type', RequirementSeverity::Warning->value)->save();
/** @var \Drupal\Core\Update\UpdateHookRegistry $update_registry */
$update_registry = \Drupal::service('update.update_hook_registry');
$update_registry->setInstalledVersion('update_script_test', $update_registry->getInstalledVersion('update_script_test') - 1);
@@ -177,7 +178,7 @@ class UpdateScriptTest extends BrowserTestBase {
// If there is a requirements error, it should be displayed even after
// clicking the link to proceed (since the problem that triggered the error
// has not been fixed).
- $update_script_test_config->set('requirement_type', REQUIREMENT_ERROR)->save();
+ $update_script_test_config->set('requirement_type', RequirementSeverity::Error->value)->save();
$this->drupalGet($this->updateUrl, ['external' => TRUE]);
$this->assertSession()->pageTextContains('This is a requirements error provided by the update_script_test module.');
$this->clickLink('try again');
@@ -185,7 +186,7 @@ class UpdateScriptTest extends BrowserTestBase {
// Ensure that changes to a module's requirements that would cause errors
// are displayed correctly.
- $update_script_test_config->set('requirement_type', REQUIREMENT_OK)->save();
+ $update_script_test_config->set('requirement_type', RequirementSeverity::OK->value)->save();
\Drupal::state()->set('update_script_test.system_info_alter', ['dependencies' => ['a_module_that_does_not_exist']]);
$this->drupalGet($this->updateUrl, ['external' => TRUE]);
$this->assertSession()->responseContains('a_module_that_does_not_exist (Missing)');
diff --git a/core/modules/system/tests/src/FunctionalJavascript/ActiveLinkTest.php b/core/modules/system/tests/src/FunctionalJavascript/ActiveLinkTest.php
index 5d2a2848aa0..4767c4d1b98 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/ActiveLinkTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/ActiveLinkTest.php
@@ -5,14 +5,14 @@ declare(strict_types=1);
namespace Drupal\Tests\system\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\Group;
/**
* Tests active link JS behavior.
*
* @see Drupal.behaviors.activeLinks
- *
- * @group system
*/
+#[Group('system')]
class ActiveLinkTest extends WebDriverTestBase {
/**
diff --git a/core/modules/system/tests/src/FunctionalJavascript/Batch/ProcessingTest.php b/core/modules/system/tests/src/FunctionalJavascript/Batch/ProcessingTest.php
index 03952729d54..9fbe8a05be0 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/Batch/ProcessingTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/Batch/ProcessingTest.php
@@ -5,10 +5,12 @@ declare(strict_types=1);
namespace Drupal\Tests\system\FunctionalJavascript\Batch;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\Group;
/**
- * @group Batch
+ * Tests Processing.
*/
+#[Group('Batch')]
class ProcessingTest extends WebDriverTestBase {
/**
diff --git a/core/modules/system/tests/src/FunctionalJavascript/CopyFieldValueTest.php b/core/modules/system/tests/src/FunctionalJavascript/CopyFieldValueTest.php
index 38728c38455..5ed27a9c42a 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/CopyFieldValueTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/CopyFieldValueTest.php
@@ -5,14 +5,14 @@ declare(strict_types=1);
namespace Drupal\Tests\system\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\Group;
/**
* Tests copy field value functionality.
*
* @see Drupal.behaviors.copyFieldValue.
- *
- * @group system
*/
+#[Group('system')]
class CopyFieldValueTest extends WebDriverTestBase {
/**
diff --git a/core/modules/system/tests/src/FunctionalJavascript/Form/ConfigTargetTest.php b/core/modules/system/tests/src/FunctionalJavascript/Form/ConfigTargetTest.php
index e9e092f23ee..5bb3f54f7d5 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/Form/ConfigTargetTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/Form/ConfigTargetTest.php
@@ -5,12 +5,12 @@ declare(strict_types=1);
namespace Drupal\Tests\system\FunctionalJavascript\Form;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\Group;
/**
* Tests forms using #config_target and #ajax together.
- *
- * @group Form
*/
+#[Group('Form')]
class ConfigTargetTest extends WebDriverTestBase {
/**
diff --git a/core/modules/system/tests/src/FunctionalJavascript/Form/DevelopmentSettingsFormTest.php b/core/modules/system/tests/src/FunctionalJavascript/Form/DevelopmentSettingsFormTest.php
index 30f50b7bd0a..f2feffa7fa8 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/Form/DevelopmentSettingsFormTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/Form/DevelopmentSettingsFormTest.php
@@ -7,13 +7,14 @@ namespace Drupal\Tests\system\FunctionalJavascript\Form;
use Drupal\Core\Cache\NullBackend;
use Drupal\Core\Url;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\Attributes\Group;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests development settings form items for expected behavior.
- *
- * @group Form
*/
+#[Group('Form')]
class DevelopmentSettingsFormTest extends WebDriverTestBase {
/**
@@ -40,9 +41,8 @@ class DevelopmentSettingsFormTest extends WebDriverTestBase {
/**
* Tests turning on Twig development mode.
- *
- * @dataProvider twigDevelopmentData
*/
+ #[DataProvider('twigDevelopmentData')]
public function testTwigDevelopmentMode(bool $twig_development_mode, ?bool $twig_debug, ?bool $twig_cache_disable): void {
$twig_debug = $twig_debug ?? $twig_development_mode;
$twig_cache_disable = $twig_cache_disable ?? $twig_development_mode;
diff --git a/core/modules/system/tests/src/FunctionalJavascript/Form/ElementsTableSelectTest.php b/core/modules/system/tests/src/FunctionalJavascript/Form/ElementsTableSelectTest.php
index 379856a5a1b..10ffd2523d3 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/Form/ElementsTableSelectTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/Form/ElementsTableSelectTest.php
@@ -5,12 +5,12 @@ declare(strict_types=1);
namespace Drupal\Tests\system\FunctionalJavascript\Form;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\Group;
/**
* Tests the tableselect form element for expected behavior.
- *
- * @group Form
*/
+#[Group('Form')]
class ElementsTableSelectTest extends WebDriverTestBase {
/**
@@ -51,7 +51,7 @@ class ElementsTableSelectTest extends WebDriverTestBase {
$this->click($row);
$this->assertSession()->assertWaitOnAjaxRequest();
$page->hasCheckedField($row);
- // Check other rows are not checked
+ // Check other rows are not checked.
for ($j = 1; $j <= 3; $j++) {
if ($j == $i) {
continue;
diff --git a/core/modules/system/tests/src/FunctionalJavascript/Form/ElementsVerticalTabsWithSummaryTest.php b/core/modules/system/tests/src/FunctionalJavascript/Form/ElementsVerticalTabsWithSummaryTest.php
index 376a9f96170..9fbe3c27995 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/Form/ElementsVerticalTabsWithSummaryTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/Form/ElementsVerticalTabsWithSummaryTest.php
@@ -5,12 +5,12 @@ declare(strict_types=1);
namespace Drupal\Tests\system\FunctionalJavascript\Form;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\Group;
/**
* Tests that titles and summaries in vertical-tabs form elements are set correctly.
- *
- * @group Form
*/
+#[Group('Form')]
class ElementsVerticalTabsWithSummaryTest extends WebDriverTestBase {
/**
diff --git a/core/modules/system/tests/src/FunctionalJavascript/Form/RebuildTest.php b/core/modules/system/tests/src/FunctionalJavascript/Form/RebuildTest.php
index 3858006154c..1ae041121d0 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/Form/RebuildTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/Form/RebuildTest.php
@@ -9,13 +9,14 @@ use Drupal\Core\Url;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\Group;
/**
* Tests functionality of \Drupal\Core\Form\FormBuilderInterface::rebuildForm().
*
- * @group Form
* @todo Add tests for other aspects of form rebuilding.
*/
+#[Group('Form')]
class RebuildTest extends WebDriverTestBase {
/**
diff --git a/core/modules/system/tests/src/FunctionalJavascript/Form/TriggeringElementTest.php b/core/modules/system/tests/src/FunctionalJavascript/Form/TriggeringElementTest.php
index d07aea57549..dec0ace7075 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/Form/TriggeringElementTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/Form/TriggeringElementTest.php
@@ -5,12 +5,12 @@ declare(strict_types=1);
namespace Drupal\Tests\system\FunctionalJavascript\Form;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\Group;
/**
* Tests that FAPI correctly determines the triggering element.
- *
- * @group Form
*/
+#[Group('Form')]
class TriggeringElementTest extends WebDriverTestBase {
/**
diff --git a/core/modules/system/tests/src/FunctionalJavascript/FrameworkTest.php b/core/modules/system/tests/src/FunctionalJavascript/FrameworkTest.php
index 0fd5f468fb1..780b772932f 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/FrameworkTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/FrameworkTest.php
@@ -5,12 +5,12 @@ declare(strict_types=1);
namespace Drupal\Tests\system\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\Group;
/**
* Tests the off-canvas dialog functionality.
- *
- * @group system
*/
+#[Group('system')]
class FrameworkTest extends WebDriverTestBase {
/**
diff --git a/core/modules/system/tests/src/FunctionalJavascript/ModalRendererTest.php b/core/modules/system/tests/src/FunctionalJavascript/ModalRendererTest.php
index ab5735efc57..5d6e8a469ba 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/ModalRendererTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/ModalRendererTest.php
@@ -5,12 +5,12 @@ declare(strict_types=1);
namespace Drupal\Tests\system\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\Group;
/**
* Tests that dialog links use different renderer services.
- *
- * @group system
*/
+#[Group('system')]
class ModalRendererTest extends WebDriverTestBase {
/**
diff --git a/core/modules/system/tests/src/FunctionalJavascript/ModuleFilterTest.php b/core/modules/system/tests/src/FunctionalJavascript/ModuleFilterTest.php
index 96f877179e3..cb809502830 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/ModuleFilterTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/ModuleFilterTest.php
@@ -5,13 +5,13 @@ declare(strict_types=1);
namespace Drupal\Tests\system\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\Group;
/**
* Tests the JavaScript functionality of the module filter.
- *
- * @group system
- * @group #slow
*/
+#[Group('system')]
+#[Group('#slow')]
class ModuleFilterTest extends WebDriverTestBase {
/**
diff --git a/core/modules/system/tests/src/FunctionalJavascript/ModuleUninstallFilterTest.php b/core/modules/system/tests/src/FunctionalJavascript/ModuleUninstallFilterTest.php
index 2652b322578..d8b3bf0eac1 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/ModuleUninstallFilterTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/ModuleUninstallFilterTest.php
@@ -5,12 +5,12 @@ declare(strict_types=1);
namespace Drupal\Tests\system\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\Group;
/**
* Tests the JavaScript functionality of the module uninstall filter.
- *
- * @group system
*/
+#[Group('system')]
class ModuleUninstallFilterTest extends WebDriverTestBase {
/**
diff --git a/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
index aef99eb3901..462bf5a9e67 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
@@ -4,11 +4,13 @@ declare(strict_types=1);
namespace Drupal\Tests\system\FunctionalJavascript;
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\Attributes\Group;
+
/**
* Tests the off-canvas dialog functionality.
- *
- * @group system
*/
+#[Group('system')]
class OffCanvasTest extends OffCanvasTestBase {
/**
@@ -34,9 +36,8 @@ class OffCanvasTest extends OffCanvasTestBase {
/**
* Tests that non-contextual links will work with the off-canvas dialog.
- *
- * @dataProvider themeDataProvider
*/
+ #[DataProvider('themeDataProvider')]
public function testOffCanvasLinks($theme): void {
$this->enableTheme($theme);
$this->drupalGet('/off-canvas-test-links');
@@ -95,7 +96,7 @@ class OffCanvasTest extends OffCanvasTestBase {
$page->clickLink('Display more links!');
$this->waitForOffCanvasToOpen();
$web_assert->linkExists('Off_canvas link!');
- // Click off-canvas link inside off-canvas dialog
+ // Click off-canvas link inside off-canvas dialog.
$page->clickLink('Off_canvas link!');
$this->waitForOffCanvasToOpen();
$web_assert->elementTextContains('css', '.ui-dialog[aria-describedby="drupal-off-canvas"]', 'Thing 2 says hello');
@@ -108,7 +109,7 @@ class OffCanvasTest extends OffCanvasTestBase {
$page->clickLink('Display more links!');
$this->waitForOffCanvasToOpen();
$web_assert->linkExists('Off_canvas link!');
- // Click off-canvas link inside off-canvas dialog
+ // Click off-canvas link inside off-canvas dialog.
$page->clickLink('Off_canvas link!');
$this->waitForOffCanvasToOpen();
$web_assert->elementTextContains('css', '.ui-dialog[aria-describedby="drupal-off-canvas"]', 'Thing 2 says hello');
diff --git a/core/modules/system/tests/src/FunctionalJavascript/System/DateFormatTest.php b/core/modules/system/tests/src/FunctionalJavascript/System/DateFormatTest.php
index d2ab2f4a744..1bbb86d92e6 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/System/DateFormatTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/System/DateFormatTest.php
@@ -6,12 +6,12 @@ namespace Drupal\Tests\system\FunctionalJavascript\System;
use Drupal\Core\Datetime\Entity\DateFormat;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use PHPUnit\Framework\Attributes\Group;
/**
* Tests that date formats UI with JavaScript enabled.
- *
- * @group system
*/
+#[Group('system')]
class DateFormatTest extends WebDriverTestBase {
/**
diff --git a/core/modules/system/tests/src/FunctionalJavascript/ThemeSettingsFormTest.php b/core/modules/system/tests/src/FunctionalJavascript/ThemeSettingsFormTest.php
index 41fc94d4f1e..9c94c6040b0 100644
--- a/core/modules/system/tests/src/FunctionalJavascript/ThemeSettingsFormTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/ThemeSettingsFormTest.php
@@ -7,12 +7,13 @@ namespace Drupal\Tests\system\FunctionalJavascript;
use Drupal\file\Entity\File;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Tests\TestFileCreationTrait;
+use PHPUnit\Framework\Attributes\DataProvider;
+use PHPUnit\Framework\Attributes\Group;
/**
* Tests that theme form settings works correctly.
- *
- * @group system
*/
+#[Group('system')]
class ThemeSettingsFormTest extends WebDriverTestBase {
use TestFileCreationTrait;
@@ -39,9 +40,8 @@ class ThemeSettingsFormTest extends WebDriverTestBase {
/**
* Tests that submission handler works correctly.
- *
- * @dataProvider providerTestFormSettingsSubmissionHandler
*/
+ #[DataProvider('providerTestFormSettingsSubmissionHandler')]
public function testFormSettingsSubmissionHandler($theme): void {
\Drupal::service('theme_installer')->install([$theme]);
diff --git a/core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php b/core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php
index 6c8c42da59e..82d866e985e 100644
--- a/core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php
+++ b/core/modules/system/tests/src/Kernel/DateFormatAccessControlHandlerTest.php
@@ -77,6 +77,8 @@ class DateFormatAccessControlHandlerTest extends KernelTestBase {
* An array of test cases.
*/
public static function providerTestAccess(): array {
+ $originalContainer = \Drupal::hasContainer() ? \Drupal::getContainer() : NULL;
+
$c = new ContainerBuilder();
$cache_contexts_manager = (new Prophet())->prophesize(CacheContextsManager::class);
$cache_contexts_manager->assertValidTokens()->willReturn(TRUE);
@@ -84,7 +86,7 @@ class DateFormatAccessControlHandlerTest extends KernelTestBase {
$c->set('cache_contexts_manager', $cache_contexts_manager);
\Drupal::setContainer($c);
- return [
+ $data = [
'No permission + unlocked' => [
[],
'unlocked',
@@ -122,6 +124,13 @@ class DateFormatAccessControlHandlerTest extends KernelTestBase {
AccessResult::allowed()->addCacheContexts(['user.permissions']),
],
];
+
+ // Restore the original container if needed.
+ if ($originalContainer) {
+ \Drupal::setContainer($originalContainer);
+ }
+
+ return $data;
}
}
diff --git a/core/modules/system/tests/src/Kernel/Element/StatusReportPageTest.php b/core/modules/system/tests/src/Kernel/Element/StatusReportPageTest.php
new file mode 100644
index 00000000000..630a3a997dd
--- /dev/null
+++ b/core/modules/system/tests/src/Kernel/Element/StatusReportPageTest.php
@@ -0,0 +1,58 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\system\Kernel\Element;
+
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\system\Element\StatusReportPage;
+
+include_once \DRUPAL_ROOT . '/core/includes/install.inc';
+
+/**
+ * Tests the status report page element.
+ *
+ * @group system
+ * @group legacy
+ */
+class StatusReportPageTest extends KernelTestBase {
+
+ /**
+ * Tests the status report page element.
+ */
+ public function testPeRenderCounters(): void {
+ $element = [
+ '#requirements' => [
+ 'foo' => [
+ 'title' => 'Foo',
+ 'severity' => \REQUIREMENT_INFO,
+ ],
+ 'baz' => [
+ 'title' => 'Baz',
+ 'severity' => RequirementSeverity::Warning,
+ ],
+ 'wiz' => [
+ 'title' => 'Wiz',
+ 'severity' => RequirementSeverity::Error,
+ ],
+ ],
+ ];
+ $this->expectDeprecation('Calling Drupal\system\Element\StatusReportPage::preRenderCounters() with an array of $requirements with \'severity\' with values not of type Drupal\Core\Extension\Requirement\RequirementSeverity enums is deprecated in drupal:11.2.0 and is required in drupal:12.0.0. See https://www.drupal.org/node/3410939');
+ $element = StatusReportPage::preRenderCounters($element);
+
+ $error = $element['#counters']['error'];
+ $this->assertEquals(1, $error['#amount']);
+ $this->assertEquals('error', $error['#severity']);
+
+ $warning = $element['#counters']['warning'];
+ $this->assertEquals(1, $warning['#amount']);
+ $this->assertEquals('warning', $warning['#severity']);
+
+ $checked = $element['#counters']['checked'];
+ $this->assertEquals(1, $checked['#amount']);
+ $this->assertEquals('checked', $checked['#severity']);
+
+ }
+
+}
diff --git a/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateSystemConfigurationTest.php b/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateSystemConfigurationTest.php
index b8b18a5c9ba..9a69e563f01 100644
--- a/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateSystemConfigurationTest.php
+++ b/core/modules/system/tests/src/Kernel/Migrate/d7/MigrateSystemConfigurationTest.php
@@ -27,8 +27,7 @@ class MigrateSystemConfigurationTest extends MigrateDrupal7TestBase {
'system.authorize' => [],
'system.cron' => [
'threshold' => [
- // Auto-run is not handled by the migration.
- // 'autorun' => 0,
+ // Auto-run is not handled by the migration, so ignore "'autorun' => 0".
'requirements_warning' => 172800,
'requirements_error' => 1209600,
],
diff --git a/core/modules/system/tests/src/Kernel/Module/RequirementsTest.php b/core/modules/system/tests/src/Kernel/Module/RequirementsTest.php
index 2258b08bc49..c22529a72db 100644
--- a/core/modules/system/tests/src/Kernel/Module/RequirementsTest.php
+++ b/core/modules/system/tests/src/Kernel/Module/RequirementsTest.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Drupal\Tests\system\Kernel\Module;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\KernelTests\KernelTestBase;
/**
@@ -28,7 +29,7 @@ class RequirementsTest extends KernelTestBase {
$requirements = $this->container->get('system.manager')->listRequirements();
// @see requirements1_test_requirements_alter()
$this->assertEquals('Requirements 1 Test - Changed', $requirements['requirements1_test_alterable']['title']);
- $this->assertEquals(REQUIREMENT_WARNING, $requirements['requirements1_test_alterable']['severity']);
+ $this->assertEquals(RequirementSeverity::Warning, $requirements['requirements1_test_alterable']['severity']);
$this->assertArrayNotHasKey('requirements1_test_deletable', $requirements);
}
diff --git a/core/modules/system/tests/src/Kernel/System/CronQueueTest.php b/core/modules/system/tests/src/Kernel/System/CronQueueTest.php
index 96a02f8f164..069a26c3eb5 100644
--- a/core/modules/system/tests/src/Kernel/System/CronQueueTest.php
+++ b/core/modules/system/tests/src/Kernel/System/CronQueueTest.php
@@ -70,7 +70,6 @@ class CronQueueTest extends KernelTestBase {
parent::setUp();
$this->connection = Database::getConnection();
- $this->cron = \Drupal::service('cron');
$time = $this->prophesize('Drupal\Component\Datetime\TimeInterface');
$time->getCurrentTime()->willReturn($this->currentTime);
@@ -91,6 +90,8 @@ class CronQueueTest extends KernelTestBase {
});
$this->container->set('queue', $queue_factory->reveal());
+ // Instantiate the `cron` service after the mock queue factory is set.
+ $this->cron = \Drupal::service('cron');
}
/**
diff --git a/core/modules/system/tests/src/Kernel/System/RunTimeRequirementsTest.php b/core/modules/system/tests/src/Kernel/System/RunTimeRequirementsTest.php
index af027b48051..e39e509cb14 100644
--- a/core/modules/system/tests/src/Kernel/System/RunTimeRequirementsTest.php
+++ b/core/modules/system/tests/src/Kernel/System/RunTimeRequirementsTest.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Drupal\Tests\system\Kernel\System;
+use Drupal\Core\Extension\Requirement\RequirementSeverity;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\KernelTests\KernelTestBase;
@@ -31,7 +32,7 @@ class RunTimeRequirementsTest extends KernelTestBase {
'title' => 'RuntimeError',
'value' => 'None',
'description' => 'Runtime Error.',
- 'severity' => REQUIREMENT_ERROR,
+ 'severity' => RequirementSeverity::Error,
];
$requirements = \Drupal::service('system.manager')->listRequirements()['test.runtime.error'];
$this->assertEquals($testRequirements, $requirements);
@@ -40,7 +41,7 @@ class RunTimeRequirementsTest extends KernelTestBase {
'title' => 'RuntimeWarning',
'value' => 'None',
'description' => 'Runtime Warning.',
- 'severity' => REQUIREMENT_WARNING,
+ 'severity' => RequirementSeverity::Warning,
];
$requirementsAlter = \Drupal::service('system.manager')->listRequirements()['test.runtime.error.alter'];
$this->assertEquals($testRequirementsAlter, $requirementsAlter);
diff --git a/core/modules/system/tests/src/Unit/Event/SecurityFileUploadEventSubscriberTest.php b/core/modules/system/tests/src/Unit/Event/SecurityFileUploadEventSubscriberTest.php
index 79b9f52812e..2cca7450089 100644
--- a/core/modules/system/tests/src/Unit/Event/SecurityFileUploadEventSubscriberTest.php
+++ b/core/modules/system/tests/src/Unit/Event/SecurityFileUploadEventSubscriberTest.php
@@ -85,12 +85,17 @@ class SecurityFileUploadEventSubscriberTest extends UnitTestCase {
'no extension produces no errors' => ['foo', '', 'foo'],
'filename is munged' => ['foo.phar.png.php.jpg', 'jpg png', 'foo.phar_.png_.php_.jpg'],
'filename is munged regardless of case' => ['FOO.pHAR.PNG.PhP.jpg', 'jpg png', 'FOO.pHAR_.PNG_.PhP_.jpg'],
- 'null bytes are removed' => ['foo' . chr(0) . '.txt' . chr(0), '', 'foo.txt'],
+ 'null bytes are removed even if some extensions are allowed' => [
+ 'foo' . chr(0) . '.html' . chr(0),
+ 'txt',
+ 'foo.html',
+ ],
'dot files are renamed' => ['.git', '', 'git'],
- 'htaccess files are renamed even if allowed' => ['.htaccess', 'htaccess txt', '.htaccess_.txt', '.htaccess'],
+ 'htaccess files are renamed even if allowed' => ['.htaccess', 'htaccess txt', 'htaccess'],
'.phtml extension allowed with .phtml file' => ['foo.phtml', 'phtml', 'foo.phtml'],
'.phtml, .txt extension allowed with .phtml file' => ['foo.phtml', 'phtml txt', 'foo.phtml_.txt', 'foo.phtml'],
'All extensions allowed with .phtml file' => ['foo.phtml', '', 'foo.phtml_.txt', 'foo.phtml'],
+ 'dot files are renamed even if allowed and not in security list' => ['.git', 'git', 'git'],
];
}
@@ -147,18 +152,10 @@ class SecurityFileUploadEventSubscriberTest extends UnitTestCase {
// The following filename would be rejected by 'FileExtension' constraint
// and therefore remains unchanged.
'.php is not munged when it would be rejected' => ['foo.php.php', 'jpg'],
- '.php is not munged when it would be rejected and filename contains null byte character' => [
- 'foo.' . chr(0) . 'php.php',
- 'jpg',
- ],
'extension less files are not munged when they would be rejected' => [
'foo',
'jpg',
],
- 'dot files are not munged when they would be rejected' => [
- '.htaccess',
- 'jpg png',
- ],
];
}
diff --git a/core/modules/system/tests/src/Unit/Pager/PreprocessPagerTest.php b/core/modules/system/tests/src/Unit/Pager/PreprocessPagerTest.php
index ab42b418125..8a2cfe22f03 100644
--- a/core/modules/system/tests/src/Unit/Pager/PreprocessPagerTest.php
+++ b/core/modules/system/tests/src/Unit/Pager/PreprocessPagerTest.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Drupal\Tests\system\Unit\Pager;
use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Pager\PagerPreprocess;
use Drupal\Core\Template\AttributeString;
use Drupal\Tests\UnitTestCase;
@@ -12,10 +13,17 @@ use Drupal\Tests\UnitTestCase;
* Tests pager preprocessing.
*
* @group system
+ *
+ * @coversDefaultClass \Drupal\Core\Pager\PagerPreprocess
*/
class PreprocessPagerTest extends UnitTestCase {
/**
+ * Pager preprocess instance.
+ */
+ protected PagerPreprocess $pagerPreprocess;
+
+ /**
* {@inheritdoc}
*/
protected function setUp(): void {
@@ -39,21 +47,19 @@ class PreprocessPagerTest extends UnitTestCase {
$pager_manager->method('getPager')->willReturn($pager);
$pager_manager->method('getUpdatedParameters')->willReturn('');
+ $this->pagerPreprocess = new PagerPreprocess($pager_manager);
+
$container = new ContainerBuilder();
- $container->set('pager.manager', $pager_manager);
$container->set('url_generator', $url_generator);
- // template_preprocess_pager() renders translatable attribute values.
- $container->set('string_translation', $this->getStringTranslationStub());
\Drupal::setContainer($container);
}
/**
- * Tests template_preprocess_pager() when an empty #quantity is passed.
+ * Tests when an empty #quantity is passed.
*
- * @covers ::template_preprocess_pager
+ * @covers ::preprocessPager
*/
public function testQuantityNotSet(): void {
- require_once $this->root . '/core/includes/theme.inc';
$variables = [
'pager' => [
'#element' => '',
@@ -63,18 +69,17 @@ class PreprocessPagerTest extends UnitTestCase {
'#tags' => '',
],
];
- template_preprocess_pager($variables);
+ $this->pagerPreprocess->preprocessPager($variables);
$this->assertEquals(['first', 'previous'], array_keys($variables['items']));
}
/**
- * Tests template_preprocess_pager() when a #quantity value is passed.
+ * Tests when a #quantity value is passed.
*
- * @covers ::template_preprocess_pager
+ * @covers ::preprocessPager
*/
public function testQuantitySet(): void {
- require_once $this->root . '/core/includes/theme.inc';
$variables = [
'pager' => [
'#element' => '2',
@@ -84,7 +89,7 @@ class PreprocessPagerTest extends UnitTestCase {
'#tags' => '',
],
];
- template_preprocess_pager($variables);
+ $this->pagerPreprocess->preprocessPager($variables);
$this->assertEquals(['first', 'previous', 'pages'], array_keys($variables['items']));
/** @var \Drupal\Core\Template\AttributeString $attribute */
@@ -94,12 +99,11 @@ class PreprocessPagerTest extends UnitTestCase {
}
/**
- * Tests template_preprocess_pager() when an empty #pagination_heading_level value is passed.
+ * Tests when an empty #pagination_heading_level value is passed.
*
- * @covers ::template_preprocess_pager
+ * @covers ::preprocessPager
*/
public function testEmptyPaginationHeadingLevelSet(): void {
- require_once $this->root . '/core/includes/theme.inc';
$variables = [
'pager' => [
'#element' => '2',
@@ -110,18 +114,17 @@ class PreprocessPagerTest extends UnitTestCase {
'#tags' => '',
],
];
- template_preprocess_pager($variables);
+ $this->pagerPreprocess->preprocessPager($variables);
$this->assertEquals('h4', $variables['pagination_heading_level']);
}
/**
- * Tests template_preprocess_pager() when no #pagination_heading_level is passed.
+ * Tests when no #pagination_heading_level is passed.
*
- * @covers ::template_preprocess_pager
+ * @covers ::preprocessPager
*/
public function testPaginationHeadingLevelNotSet(): void {
- require_once $this->root . '/core/includes/theme.inc';
$variables = [
'pager' => [
'#element' => '',
@@ -131,18 +134,17 @@ class PreprocessPagerTest extends UnitTestCase {
'#tags' => '',
],
];
- template_preprocess_pager($variables);
+ $this->pagerPreprocess->preprocessPager($variables);
$this->assertEquals('h4', $variables['pagination_heading_level']);
}
/**
- * Tests template_preprocess_pager() when a #pagination_heading_level value is passed.
+ * Tests when a #pagination_heading_level value is passed.
*
- * @covers ::template_preprocess_pager
+ * @covers ::preprocessPager
*/
public function testPaginationHeadingLevelSet(): void {
- require_once $this->root . '/core/includes/theme.inc';
$variables = [
'pager' => [
'#element' => '2',
@@ -153,18 +155,17 @@ class PreprocessPagerTest extends UnitTestCase {
'#tags' => '',
],
];
- template_preprocess_pager($variables);
+ $this->pagerPreprocess->preprocessPager($variables);
$this->assertEquals('h5', $variables['pagination_heading_level']);
}
/**
- * Test template_preprocess_pager() with an invalid #pagination_heading_level.
+ * Test with an invalid #pagination_heading_level.
*
- * @covers ::template_preprocess_pager
+ * @covers ::preprocessPager
*/
public function testPaginationHeadingLevelInvalid(): void {
- require_once $this->root . '/core/includes/theme.inc';
$variables = [
'pager' => [
'#element' => '2',
@@ -175,7 +176,7 @@ class PreprocessPagerTest extends UnitTestCase {
'#tags' => '',
],
];
- template_preprocess_pager($variables);
+ $this->pagerPreprocess->preprocessPager($variables);
$this->assertEquals('h4', $variables['pagination_heading_level']);
}
diff --git a/core/modules/system/tests/themes/test_theme/test_theme.theme b/core/modules/system/tests/themes/test_theme/test_theme.theme
index 80214af02d0..d0b3b2b71bf 100644
--- a/core/modules/system/tests/themes/test_theme/test_theme.theme
+++ b/core/modules/system/tests/themes/test_theme/test_theme.theme
@@ -54,7 +54,7 @@ function test_theme_theme_suggestions_alter(array &$suggestions, array &$variabl
// the theme_suggestions_test module can be picked up when that module is
// enabled.
if ($hook == 'theme_test_general_suggestions') {
- array_unshift($suggestions, 'theme_test_general_suggestions__' . 'theme_override');
+ array_unshift($suggestions, 'theme_test_general_suggestions__theme_override');
$variables['theme_hook'] = 'test_theme_theme_suggestions_alter';
}
}
@@ -68,7 +68,7 @@ function test_theme_theme_suggestions_theme_test_suggestions_alter(array &$sugge
// suggestion to the beginning of the array so that the suggestion added by
// the theme_suggestions_test module can be picked up when that module is
// enabled.
- array_unshift($suggestions, 'theme_test_suggestions__' . 'theme_override');
+ array_unshift($suggestions, 'theme_test_suggestions__theme_override');
}
/**