There are several calls to unserialize() in the migration modules (migrate, migrate_drupal, and migrate_drupal_ui). We should add the optional $options parameter to most or all of these, setting allowed_classes.
Token replace takes a callback function, which is expected to be a string. This makes it difficult to be used for classes with methods. This problem was raised when doing the Token contrib module port to D8.
Token::replace() accepts a callable in its $options['callback'] parameter:
1) The parameter does not clearly state what the callback should do.
2) The code keeps calling the callback a function, while it technically accepts any kind of callable, as it should.
The core system for retrieving the active menu trail does not check if the matching menu link is enabled or disabled. This can lead to several problems when a menu link is disabled, such as an "active" class inappropriately set on a parent menu link, or a wrong breadcrumb if Menu Breadcrumb is used.
A similar problem can happen if the matching menu link is active but one of its parent links is disabled. But:
* this would be a very strange use case,
* I don't know what we should expect from such a configuration,
* in that case, the problem could be easily avoided by also disabling the matching menu link,
* handle this would require to load all the parents to check if they are enabled, which might harm performance.
So I guess we only need to handle the case where the matching menu link is disabled. Here's a patch.
Steps to reproduce
Create menu items with this structure in the main menu:
Node 1 (enabled)
Node 2 (disabled)
Browse to Node 2 and inspect the menu markup: the Node 1 menu item has the primary-nav__menu-link--active-trail class.
Proposed resolution
Disabled menu items should not be used to generate the active trail.
Downstream systems are being updated to support this. If Drupal sends a request that exceeds 16k the downstream systems will fail with inconclusive errors. For example Varnish will return "HTTP/1.1 502 Bad Gateway." Very few people will be able to debug this sort of situation. We recently encountered a situation with a 50k header!
If a request has too many cache-tags associated with it the header size could exceed 16kb. When this occurs Drupal should automatically split the header into multiple headers that fit within the maximum length.
Steps to reproduce
Enable http.response.debug_cacheability_headers: true and look for a page with many related cache tags so the X-Drupal-Cache-Tags header is larger than 8K.
Proposed resolution
Limit X-Drupal-Cache-Tags and X-Drupal-Cache-Contexts headers to 8K.
If any header is bigger than that split the value into different header with an autoincrement: X-Drupal-Cache-Contexts-1, X-Drupal-Cache-Contexts-2 and so on.
Apply this to any headers that potentially exceed the maximum length.
Update documentation.
Remaining tasks
None
User interface changes
None.
Introduced terminology
None.
API changes
None.
Data model changes
None.
Release notes snippet
Headers exceeding the maximum length of 8000 characters will be automatically split into multiple header lines. For example X-Drupal-Cache-Tags, X-Drupal-Cache-Tags-1, X-Drupal-Cache-Tags-2 could be in the response if the number of cache tags is very high and debug_cacheability_headers is enabled in *.services.yml
Before running rector, you need to comment out a line in the rector config class otherwise the @var docs will be copied into the constructor signature which is extremely ugly.
Comment out this line: $this->propertyPromotionDocBlockMerger->mergePropertyAndParamDocBlocks($property, $param, $paramTagValueNode);
1. Run the following rector:
2. Rector will put the opening brace of the constructor onto a new line. To fix this run phpcbf with just the relevant sniff: ./bin/phpcbf --standard=app/core/phpcs.xml.dist --sniffs=Drupal.Functions.MultiLineFunctionDeclaration app/[core|composer]
Do not try to rebase the MR, simply run the steps again.
Remaining tasks
Write a rector rule to help us with this.
Decide if we need to document constructor parameters any more or if that is also unnecessary boilerplate.
Discover any edge cases and figure out what to do with them.
One concern mentioned there is that $entity->get('field_name') throws an exception, while $entity->field_name does not.
This results in pretty unwieldy code:
if ($entity->hasField('field_name') && $entity->get('field_name')->value)
This made sense many years ago when it was added, but since then we have nullable operator, so if we remove the exception, we can do this:
if ($entity->get('field_name')?->value)
Steps to reproduce
Proposed resolution
Add a second boolean parameter $exception_on_invalid to FieldableEntityInterface::get().
For Drupal 11.3+, use func_get_args() to get argument value. If there is no second parameter passed, default the value to TRUE, so that the same functionality is kept and an exception is thrown on an invalid field name. If the second parameter is passed as FALSE, the method will return NULL on an invalid field name.
In Drupal 12, add the second parameter and keep the second parameter default as TRUE. Then, going forward get('{invalid field name}') will throw an exception and get('{invalid field name}', FALSE) returns NULL
In Drupal 13, add the second parameter and flip the second parameter to default as FALSE. Then, going forward get('{invalid field name}') will return NULL and get('{invalid field name}', TRUE) will throw and exception
MR 10406: $exception_on_invalid default is TRUE with the expectation it will continue to default to TRUE
MR 12123: $exception_on_invalid default is TRUE with the expectation it will be changed to default to FALSE in Drupal 13.
Remaining tasks
Decide between the two approaches.
Also, two things came up resolving tests that probably need answers:
ContentEntityBase::set() calls get(). We probably either need to do the same with set() and add an optional $exception_on_invalid so it can be passed to get(), or we have set() call get() explicitly with $exception_on_invalid as TRUE, so exceptions will always be thrown for invalid fields
get() is documented in FieldableEntityInterface to throw an InvalidArgumentException on invalid field names, but in ContentEntityBase::getTranslatedField, which is called by get(), it's also possible to throw an exception when "The entity object refers to a removed translation". Should this exception be suppressed as well, or only ones with message "Field $name is unknown"? Basing conditional logic on exception messages seems brittle
Drupal saves and render Date field in certain way. However the way it does, is not very transparent looking at the available documentation. This becomes critical when we have time sensitive fields example Event Date and Time.
During content save Drupal will store Date field in UTC format, the date input by user is considered to be in timezone user belongs to at the time of saving.
During content render Drupal will convert it to viewer's user account timezone in case of logged in user, else in site default time zone in case of anonymous user.
While this works most of the times, however in case where content editor has to create multi geographical data belonging to different timezone it becomes erratic in nature and hard to understand for editor.
Steps to reproduce
1. Add a Date and Time Field in Drupal.
2. The process of Drupal storing and rendering date field is missing.
Proposed resolution
Improve documentation to explain Drupal way of maintaining the date, so site builders take more aware decisions. Image may be NSFW. Clik here to view.
This meeting takes place every Friday at 14:00 UTC (currently 6:00am PT, 9:00am ET). See Time.is to see what that is in your timezone.
The meetings are held using Zoom, and a link is posted in the #ux Slack channel 10 minutes before the meeting. Agenda is first come, first serve and set by attendees. Use the Needs usability review issue tag for issues that need review and/or suggest issues in comments here.
List of Slack users to ping 10 minutes before the meeting:
@worldlinemine, @lauriii, @AaronMcHale, @anmolgoyal74, @Ravi, @shaal, @ckrina, @simohell, @gauravvv, @Quynh, @yoroy, @andrei.zvonkov, @Regu.pl
This list gets copied to the issue for the next meeting. If that has already happened, then go to that issue to add/remove yourself to/from the list.
@AaronMcHale: Issue could use some guidance. In comment #109 the issue was marked as postponed for needing input from UX, accessibility and media initiative. Apparently there are some outstanding UX/accessibility questions (but I'm not sure what they are). This was echoed in comment #111. Comment #144 is asking what is needed to move the issue along (the postponed state seems to be causing some delays/confusion as to how to proceed). Perhaps we can at least take a look at what the issue is doing and provide some recommendations/guidance on how it could be implemented.
This meeting takes place every Friday at 14:00 UTC (currently 7:00am PT, 10:00am ET). See Time.is to see what that is in your timezone.
The meetings are held using Zoom, and a link is posted in the #ux Slack channel 10 minutes before the meeting. Agenda is first come, first serve and set by attendees. Use the Needs usability review issue tag for issues that need review and/or suggest issues in comments here.
List of Slack users to ping 10 minutes before the meeting:
@Gábor Hojtsy (he/him), @worldlinemine, @lauriii, @AaronMcHale, @anmolgoyal74, @Antoniya, @Ravi, @shaal, @ckrina, @simohell, @gauravvv, @penyaskito, @Mike Gifford (CivicActions), @April, @Quynh, @yoroy, @EricRubino
Go to the issue for the next meeting to add/remove yourself to/from the list.
@AaronMcHale: Issue could use some guidance. In comment #109 the issue was marked as postponed for needing input from UX, accessibility and media initiative. Apparently there are some outstanding UX/accessibility questions (but I'm not sure what they are). This was echoed in comment #111. Comment #144 is asking what is needed to move the issue along (the postponed state seems to be causing some delays/confusion as to how to proceed). Perhaps we can at least take a look at what the issue is doing and provide some recommendations/guidance on how it could be implemented.
Visit the "Manage fields" tab for an entity type and bundle. (For example: /admin/structure/types/manage/page/fields for the Page content type.)
Use the "Create a new field" action link.
Choose a type of field, such as Reference.
This issue deals with the text in the resulting modal window that helps the site builder choose the correct field type.
Proposed resolution
We discussed the text for field-type descriptions during several Usability meetings, and we tracked our recommendation in a spreadsheet: https://docs.google.com/spreadsheets/d/1Lx7L40eRHotr5KQGn6au5WxQO3lFv19a.... There are a few common themes: use examples; replace "Ideal for" with "Recommended for"; use the bullet points (unordered lists) for differentiators. Most of the changes in MR !11153 are simple: they update the text, including updates to the automated tests as needed.
A few of the changes are worth explaining:
Add an optional summary paragraph just above the label "Choose a field type". This requires adding the getSummary() method to Drupal\Core\Field\FieldTypeCategoryInterface and Drupal\Core\Field\FieldTypeCategory and some updates to FieldTypeCategoryManager to support the summary and a change to Drupal\field_ui\Form\FieldStorageAddForm to use the summary.
Since FieldStorageAddForm::buildForm() was already very long, move the part for listing all the field types in a selected group to the new protected method buildGroupForm(). That change was done in a separate commit, so it can be reverted and moved to a follow-up issue if the refactoring makes it too hard to review the MR.
In order to get different text for each of the Reference types, add the optional description key to the array returned by Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface::getPreconfiguredOptions(). Provide a value for that key by implementing hook_field_ui_preconfigured_options_alter.
The method Drupal\Tests\field_ui\Traits\FieldUiJSTestTrait::fieldUIAddNewFieldJS() tried to find a link containing the field-type label. It assumed that if the field type was part of a group, then none of the links would match. This MR simplifies the labels, so that assumption no longer holds. So the MR updates that method. It now checks whether the field type is part of a group before looking for a link.
As an example of (1), the modal window for selecting a Selection list field now has the summary
Each list item has a Name, with limited formatting, and a Value. Values that are in use cannot be changed, since they are stored in the database. The Names can be edited later.
The "Add field: Reference" modal (after choosing "Reference" in Step 3 in the Steps to reproduce):
Image may be NSFW. Clik here to view.
The "Add field: Selection list" modal (after choosing "Selection list" in Step 3 in the Steps to reproduce):
Image may be NSFW. Clik here to view.
API changes
Add the getSummary() method to Drupal\Core\Field\FieldTypeCategoryInterface. This is an API addition, and there is only one class that implements the interface.
Add the optional description key to the array returned by Drupal\Core\Field\PreconfiguredFieldUiOptionsInterface::getPreconfiguredOptions(). Since the return type is mixed[][], this does not really count as an API change.
The Drupal Core products strategy builds on the Drupal Starshot product strategy, which you can find at https://dri.es/introducing-drupal-starshot-product-strategy. Together, these two strategy documents aim to provide a more complete vision for the Drupal project, fostering alignment across the community and leadership.
The Drupal Core strategy document has been a collaborative effort among the Core Committers. Through a mix of in-person meetings, Zoom calls, and asynchronous discussions in Slack and Google Docs, we've worked together to shape a shared vision.
While this strategy is still a work in progress, we've reached a critical milestone: it’s time to broaden the conversation and get feedback. We're seeking input from the community to ensure the strategy resonates and aligns with the needs of everyone invested in Drupal's success. To provide feedback, download the PDF document attached to this issue and provide comments below.
A well-defined strategy is essential for accelerating innovation, creating focus, keeping Drupal relevant, and attracting new users and contributors. At its core, strategy is about making trade-offs — choosing what to prioritize and what not to prioritize. This document is designed to guide contributions and empower our community to make decisions confidently.
We're not sharing the entire strategy document today. There is more work to be done and sections that are not ready to be shared. We're sharing what we feel aligned and good about. This allows us to focus feedback on the broader ideas and overall direction before we continue to work on the finer details.
Later this week, we'll host a Zoom meeting with Core Committers and subsystem maintainers to get feedback. In the coming months, we'll also expand our outreach to include structured feedback from other key stakeholders, such as the Drupal Association staff, Board of Directors, end users, and more. In the mean time, everyone can give feedback in this issue.
It's 2025. The render API suspiciously looks like form API when we added it 20 years ago.
Steps to reproduce
Proposed resolution
Note this issue is currently allowing failure of the following jobs since there are architectural explorations that need to happen before dealing with these standards:
PHPCS and CSPELL
Let's reuse the existing render element plugins as object wrappers around render arrays. To achieve this, let's add:
ElementInfoManager::fromClass. It's like createInstance but it takes a class instead of a plugin id. We will see why this is good in the next point.
@property documentation on the render element plugin classes based on existing documentation. This together with the previous point allows for this: Image may be NSFW. Clik here to view.
ElementInfoManager::fromRenderArray() which creates an ElementInterface plugin from a render array and stores a reference to that render array.
__set and __get to RenderElementBase to allow using render properties as object properties.
A few methods to work with children: addChild, removeChild, getChildren. These will return objects.
RenderElementBasee::toRenderArray method which returns the stored render array.
FormBase::elementInfoManager() to get the element info manager.
plugin.manager.element_info becomes central for all things dealing with render API but all hooks now can use dependency injection to get it. Widgets always been plugins which also can use DI and forms are easily dealt with as above. Maybe various twig functionality will need it too but a) preprocess is also OOP now (yay) b) twig extensions are tagged services.
PHP 8.4 -- which will likely be required soon -- introduces property hooks. So it'll be possible to have setters and getters. So it's possible to start using properties without methods and seamlessly change core to add logic to them as needed. That will also allow deprecating properties.
Note: Because I know this will come up, the current implementation creates recursion in the render array, PHP handles this gracefully in serialize and dumping too so it's not a problem any more. var_dump and print_r prints ** RECURSION **, var_export produces a warning but all three work and doesn't fall into an infinite recursion. serialize also works but to make sure it works with other serializers on sleep/wakeup the recursion is broken/rebuilt.
Remaining tasks
Standardize naming. Do we call these element plugin instances, render objects or something else?
Finish any deprecations
Update comments
Re-enable phpcs and cspell
Write tests
In a followup, add a traversal helper and then convert form the renderer/builder/validator/submit to visitors -- I think that'd be cleaner as it centralizes recursion
In the ultimate followup, convert all core and contrib to the new API and drop render arrays. (Should be an easy, simple issue.)
While we want the first stage of tests to help keep core consistent and catch issues with phpstan. There are situations such as architectural issues you would want full tests to run even if phpcs is failing for example.
I would like to suggest that if an MR is in draft then the first stage of tests get a new rule that allows failure.
We would need to ensure that code is not committed from a draft MR as well.
Neither of those are really in a position to be even in needs review, but it would be valuable to see a full test run even if there is a spelling mistake. This would have the potential to increase resources used by the DA, but we could be cognizant of when an issue uses Draft MRs
Proposed resolution
Rule to allow failure for most jobs in the first column when the MR is in Draft
I have a views list of names. The listing is filtered by an alphabetical glossary attached to the view. Default language of the site is French but available in both French and English. The view is accessed in the url "/noms". I added an alias through admin/config/search/path so the English page is accessed at "/en/names".
Everything works fine and filtering is executed as expected in both languages if Ajax is disabled.
If Ajax is enabled, only the French (default language) works. The English aliased page is no more filtering. When I pass the mouse over the glossary letters display correctly, for example: "noms/d" and "/en/names/d"
If I manually enter the aliased url with a contextual filter (i.e. /en/names/d), of course it works as this way it doesn't call ajax.
No errors appear in browser's dev console nor in Drupal's dblog report.
Steps to reproduce
Configure site as multilingual with French as default and English as additional language
Create some pages (simple nodes with a title)
Create a Views page listing the titles of the nodes created above. Set a contextual filter to filter by Content:title. Set it's path to '/noms'
Create an attachment listing the titles. Add contextual filter "content: title" and set "When the filter value is NOT available" to "Display a summary" and in "More" set "Glossary mode" and "Character limit" to 1. Finally attach the attachment to the previous page.
Go to "admin/config/search/path" and add alias: English, system path '/noms', url alias '/names'
Visit '/noms' and '/en/names' to verify that everything works. Click on glossary letters and also enter manually some filters, like: '/noms/b' and '/en/noms/b'
In Views page, set Use Ajax to Yes. Visit again the pages in French and English. French pages work as expected. On English pages, manually entering '/en/noms/b' works but clicking on the glossary letters has no effect even if the ajax spinner turns.
After the initial load of a layout builder edit page (either per-entity overrides or the per-display defaults), any subsequent visits to the page will trigger a "You have unsaved changes" message, even when no changes have been made.
Enable layout builder for a content type, and allow each content item to have its layout customized.
Create a node.
Click the "Layout" tab.
Refresh the page.
Actual result: "You have unsaved changes" message appears.
Expected result: The message should not appear, since there are no unsaved changes.
Proposed resolution
Update LayoutTempstoreRepository so that it can explicitly keep track of whether any given SectionStorage in the repository has unsaved changes.
Remaining tasks
Review
User interface changes
The "You have unsaved changes" message will only appear when you have unsaved changes
API changes
LayoutTempstoreRepositoryInterface has a new public hasUnsavedChanges() function. Note that this interface has a one-to-one correspondence with the LayoutTempstoreRepository class, so we can add functions without breaking any BC rules.
In addition, LayoutTempstoreRepositoryInterface::set() has a new optional boolean argument called $has_unsaved_changes, which is used to track whether there are unsaved changes in the SectionStorage being set.
Let's start with a very usefull contraint: Sequentially. Which allows us to layer contraints. Which could help in only reporting errors which are usefull instead of walking through all the contraints.
Bare minimum query parameters: https://example.org/views/ajax?ajax_page_state[libraries]=ajax&view_name=example_view
Proposed resolution
A patch has been provided that addresses the issue by checking if the array key exists before accessing it, thereby preventing the warnings.
Currently, the warnings appear because the code attempts to access an array key that may not be set, because they are missing the slash. A patch has been provided that prevents this by checking for the existence of the key before accessing it.
Alternatively, instead of suppressing the warning, we could throw a RuntimeException when this occurs, triggering a 4xx client error. This would indicate that the request was invalid rather than failing silently, making it clear that unexpected input has been provided.
Original Issue Summary
• Warning: Undefined array key 1 in Drupal\Core\Asset\AssetResolver->getCssAssets() (line 133 of core\lib\Drupal\Core\Asset\AssetResolver.php).
• Warning: Undefined array key 1 in Drupal\Core\Asset\AssetResolver->getJsAssets() (line 239 of core\lib\Drupal\Core\Asset\AssetResolver.php).
• Warning: Undefined array key 1 in Drupal\Core\Asset\AssetResolver->getJsAssets() (line 251 of core\lib\Drupal\Core\Asset\AssetResolver.php).
• Warning: Undefined array key 1 in Drupal\Core\Asset\AssetResolver->getJsSettingsAssets() (line 199 of core\lib\Drupal\Core\Asset\AssetResolver.php).