Quantcast
Viewing all 294476 articles
Browse latest View live

Notation of placeholders in SqlContentEntityStorageRevisionDataCleanupTest is incorrect

Problem/Motivation

From #3259709-89: [PP-1] Create the database driver for MySQLi.

Notation of placeholders in SqlContentEntityStorageRevisionDataCleanupTest is incorrect, the arguments in the queries of this test DO NOT HAVE the colon in front, i.e. it should be ':nid' => 8.

Apparently the PDO-based drivers do not care, even if https://www.drupal.org/docs/drupal-apis/database-api/static-queries#plac... examples clearly indicate placeholders should have the colon.

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet


Field selection breaks conventions and increases cognitive load

Problem/Motivation

New version of field creation adds unnecessary clutter to the form and increases cognitive load for using the form. ThIs version also breaks conventions on basic html elements. With some changes the form's usability can be easily improved.

Cognitive (gestalt) principles involved are similarity, proximity and same container. The principles are known to take precedence by similarity being overridden by proximity and proximity overridden by same container.

Radio buttons

Radio buttons are based on similarity and proximity. When a radio buttons are placed far away from each other and especially if separated into visual containers the brain will not understand them to be related. Thus here they add visual clutter and cognitive strain by not matching familiar patterns.

First step before

Image may be NSFW.
Clik here to view.

Second step before

Image may be NSFW.
Clik here to view.

Overlapping options to be addressed in a follow-up #3410851: "Selection list" should be removed from field types and given as an option for "number" and "plain text"

Field types are not well defined and hiding the options details makes user unable to select the correct type without toggling between types to read the options descriptions. Especially selection list overlaps with plain text and number. For end user "selection list" is a display widget for contend that is either text, number or reference.

Image may be NSFW.
Clik here to view.
Field dialog GUI highlighting overlap of selectable items

Steps to reproduce

Proposed resolution

  1. Radio buttons should never be used unless they are aligned vertically.
    • Just visually hide the radio buttons and use the whole box to highlight the hover or selected item.
    • Instead of using input for field type, consider using HTML summary or html_tag for type and details containing radiobuttons for options. If we must have input value for type, then consider using hidden and set the correct one based on selected option. This should be logically possible because of the hierarchy/cadinality of types vs options.
  2. Increase the attention value of selected type and its option to form a group that is recognized as strongly connected items. This could be achieved by proximity (display options next to the selected type) or strong similarity (shape, color), this is eliminated with the use of the two step form as the second step makes a focused sub option approach.
  3. "Selection list" should be removed from field types and given as an option for "number" and "plain text" (would be addressed later in a follow up #3410851: "Selection list" should be removed from field types and given as an option for "number" and "plain text")

Text content for descriptions should be reviewed in a separate task.

Remaining tasks

  1. Reduce the distance between related options which was partially addressed by #3408326: Make field selection a two-step form , as we moved to a two step form.
  2. Convert the first step into using cards as clickable items to navigate to the sub-options.
  3. Vertically align all the options on the second step so they are easier to understand.
  4. Adjust the tests for the new UI experience.

User interface changes

First step after
Now uses clickable cards which navigate to the sub-options/field storage options.

Image may be NSFW.
Clik here to view.

Second step after
Now Vertically aligned for easier scanning.

Image may be NSFW.
Clik here to view.

API changes

Data model changes

Release notes snippet

ConfigEntityType inherited functions return FALSE instead of NULL

Problem/Motivation

In ConfigEntityType.php:

  /**
   * {@inheritdoc}
   */
  public function getBaseTable() {
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function getRevisionDataTable() {
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function getRevisionTable() {
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function getDataTable() {
    return FALSE;
  }

Whereas in EntityTypeInterface, each function says:

   * @return string|null
   *   The name of the entity type's revision table, or NULL if none exists.

Proposed resolution

Return NULL instead, or update the phpdoc comments.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Use autowiring for core controllers

Problem/Motivation

Following #3394870: Allow controller service wiring via constructor parameter attributes we can autowire many classes in core that extend ControllerBase by deleting the create() method.

This provides a good example for contrib and custom code to follow, and should mean we can delete hundreds of lines of boilerplate code.

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Add Psr\EventDispatcher\EventDispatcherInterface alias to core services

Problem/Motivation

#3049525: Enable service autowiring by adding interface aliases to core service definitions added service aliases to allow for autowiring services. There is a service alias @event_dispatcher for event dispatcher (Symfony\Contracts\EventDispatcher\EventDispatcherInterface). However, in order to allow autowiring services which typehint against Psr\EventDispatcher\EventDispatcherInterface, that interface should be added as an additional alias.

Steps to reproduce

Proposed resolution

Add Psr\EventDispatcher\EventDispatcherInterface: '@event_dispatcher' to core.services.yml.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Deprecate the 'roles' property of text formats

Problem/Motivation

The roles property of text formats was thought to be a convenience originally, but it has become more of a nuisance by now.

First of all, there is #2569741: [PP-1] FilterFormat should add the roles as config dependencies. In particular, in #39 there it is made clear that having those roles there introduces a circular dependency between text formats and roles.

Furthermore, the roles are not included when exporting text formats, which leads to unintentional diffs in version control. This has long been a problem for custom sites owners, but has also struck core development: #3086965: Allow embedding media in CKEditor in Umami removed the roles property from the Basic HTML and Full HTML formats in Umami, as those were re-exported for the patch there. The Restricted HTML format in Umami and all three formats in Standard still have the roles in there, however. This inconsistency is unfortunate and should be resolved one way or the other. But instead of manually re-introducing the roles where they were removed, let's go ahead and remove them everywhere.

And if we are not using it in core and both for profiles/distributions as well as for custom sites the trend has strongly gone in favor of automatically exporting configuration over handcrafting it, we might as well deprecate it so we can resolve #2569741: [PP-1] FilterFormat should add the roles as config dependencies in Drupal 10.

Proposed resolution

Remove the roles property from all filters in core. The respective roles in Umami and Standard already have the proper permissions, so this will not be a functional change.

Subsequently add a deprecation notice in FilterFormat::get() and FilterFormat::set() for the roles property.

Remaining tasks

  1. Review patch
  2. Write change notice

User interface changes

None.

API changes

None.

Data model changes

The roles property of text formats will be deprecated.

Active trail gets corrupted in cache_menu for menus with numeric machine names

Problem/Motivation

We have a site where some menus have numeric machine names (i.e. menu title is 'Transport', but its machine name is '583'). I don't know the reason for this (probably they were migrated or created via code to match some legacy system), but this can also be created easily via menu UI.

We noticed occasional problems with these menus, causing them to disappear from the pages where they would normally appear on. After much debugging, I found out that the problem comes from corrupted entries in cache_menu (cid begins with active-trail:....), which affect menus with number-like machine names.

This issue has happened for us on multiple Drupal 9.x versions. I was able to replicate and debug it in Drupal 9.5.11, and then replicate it on 10.1.6 also.

In the following steps to reproduce this bug, I used two menus, which I called 2 and 4. Note that the bug is independent of these values.

Steps to reproduce

Phase 1. Initial corruption of the cached entries

1. install a fresh Drupal site (standard profile is enough)
2. create a basic page (e.g. /node/1)
3. create two menus with numeric machine name (call them 2 and 4 and any title you want) and one link in each:

  2
    link 2.1 (pointing to any url)
  4
    link 4.1 (pointing to any url)

4. place two blocks (e.g. in content area) to display these two menus on /node/1 (i.e. Restrict to certain pages set to /node/1)
5. go to /node/1 and confirm the menus are showing
6. check the cache_menu table and look at the active-trail entry for that page:

drush sql-query "select data from cache_menu where cid='active-trail:route:entity.node.canonical:route_parameters:a:1:{s:4:\"node\";s:1:\"1\";}'"

You should see something like this:

a:4:{s:4:"main";a:2:{s:54:"menu_link_content:53f35910-253f-4c3f-9089-abd5884416a3";s:54:"menu_link_content:53f35910-253f-4c3f-9089-abd5884416a3";s:0:"";s:0:"";}s:7:"account";a:1:{s:0:"";s:0:"";}i:2;a:1:{s:0:"";s:0:"";}i:4;a:1:{s:0:"";s:0:"";}}

Note that the last two entries in this serialized data are i:2 and i:4 (i.e. the machine names of the menus, but converted to integer values).
7. rebuild the cache using drush cr or at least clear these bins drush cc bin menu render page dynamic_page_cache
8. send multiple simultaneous requests for /node/1 page, either via browser using F5 multiple times very fast, or (better) via commands:

wget -qO /dev/null https://drupal.sandbox.local/node/1 &
wget -qO /dev/null https://drupal.sandbox.local/node/1 &
wget -qO /dev/null https://drupal.sandbox.local/node/1 &
wget -qO /dev/null https://drupal.sandbox.local/node/1 &
wget -qO /dev/null https://drupal.sandbox.local/node/1 &
wget -qO /dev/null https://drupal.sandbox.local/node/1 &

9. check again the cache_menu table:

drush sql-query "select data from cache_menu where cid='active-trail:route:entity.node.canonical:route_parameters:a:1:{s:4:\"node\";s:1:\"1\";}'"

This time you will see something like this:

a:6:{s:4:"main";a:1:{s:0:"";s:0:"";}s:7:"account";a:1:{s:0:"";s:0:"";}i:0;a:1:{s:0:"";s:0:"";}i:1;a:1:{s:0:"";s:0:"";}i:2;a:1:{s:0:"";s:0:"";}i:3;a:1:{s:0:"";s:0:"";}}

Note that i:2 and i:4 have been renumbered (by array_merge here https://github.com/drupal/core/blob/11.x/lib/Drupal/Core/Cache/CacheColl...) into i:0 and i:1 and also duplicated as i:2 and i:3. This is according to https://www.php.net/manual/en/function.array-merge.php ("Values in the input arrays with numeric keys will be renumbered with incrementing keys starting from zero in the result array."). Basically, two copies of the active trail (one created by current request and another one cache microseconds before by another request), got merged together: the text-like menus were kept (a single entry for each), but the numeric-like menus were renumbered and duplicated.

If you don't see this, try again to clear the cache and run the wget commands (maybe add some more to simulate a busier site).
Depending on the site load (i.e. number of simultaneous requests that don't find the active-trail cache entry) and the number of menus with numeric machine names, you might see tens or hundreds of such entries.

Phase 2. Additional corruption of the cached entries

Moreover, when entries in the dynamic pages cache expire, this problem will be increased even more. Each request that runs into this use case, will copy again the numeric entries. The steps to replicate this are (run them multiple times):

1. Use the steps from Phase 1. to corrupt the cache.
2. Clear page caches (don't clear entire cache as that will cancel previous step):

drush cc bin dynamic_page_cache page

3. load the /node/1 page (just one page request is enough, no need for parallel ones):

wget -qO /dev/null https://drupal.sandbox.local/node/1

4. view the cached entry

drush sql-query "select data from cache_menu where cid='active-trail:route:entity.node.canonical:route_parameters:a:1:{s:4:\"node\";s:1:\"1\";}'"

You will see this:

a:7:{s:4:"main";a:1:{s:0:"";s:0:"";}s:7:"account";a:1:{s:0:"";s:0:"";}i:0;a:1:{s:0:"";s:0:"";}i:1;a:1:{s:0:"";s:0:"";}i:2;a:1:{s:0:"";s:0:"";}i:3;a:1:{s:0:"";s:0:"";}i:4;a:1:{s:0:"";s:0:"";}}

5. Repeat previous three steps and you will see this:

a:8:{s:4:"main";a:1:{s:0:"";s:0:"";}s:7:"account";a:1:{s:0:"";s:0:"";}i:0;a:1:{s:0:"";s:0:"";}i:1;a:1:{s:0:"";s:0:"";}i:2;a:1:{s:0:"";s:0:"";}i:3;a:1:{s:0:"";s:0:"";}i:4;a:1:{s:0:"";s:0:"";}i:5;a:1:{s:0:"";s:0:"";}}

6. Repeat previous step and will see the cached entry having more and more numeric entries.

Phase 3. Disappearing blocks due to corrupted data

Eventually, if the site runs long enough with corrupted cache, the bad entries start to overwrite the correct entries coming from numeric menus, causing various problems with them (e.g. menus to disappear from site pages). This can be replicated like this:

1. edit the basic content type to allow adding these nodes to menu 2 and 4
2. edit node/1 and add a menu entry for it in menu 4
3. edit menu 4 to look like this (link 4.1 is sublink of node 1 link)

  4
    node 1 (added in step 2 before)
      link 4.1 (pointing to any url)

4. edit the block that displays menu 4 and set Initial visibility level to 2
5. clear the cache (drush cr)
6. visit /node/1 and check that link 4.1 (from menu 4) shows on that page
7. Look at the cached entry:

drush sql-query "select data from cache_menu where cid='active-trail:route:entity.node.canonical:route_parameters:a:1:{s:4:\"node\";s:1:\"1\";}'"

You will see something like this (with different UUIDs):

a:4:{s:4:"main";a:1:{s:0:"";s:0:"";}s:7:"account";a:1:{s:0:"";s:0:"";}i:2;a:1:{s:0:"";s:0:"";}i:4;a:2:{s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:0:"";s:0:"";}}

Notice the value for i:4 (menu 4) is
i:4;a:2:{s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:0:"";s:0:"";}
because the current page request is in the active trail of the menu.
8. corrupt the cache (using the steps 7-8 from Phase 1 above, but use more wget commands. I used 30)
9. visit /node/1 and check that link 4.1 disappeared from the page (you might need to run previous step multiple times to make it happen, or just use more wgets).
10. Look at the cached entry:

drush sql-query "select data from cache_menu where cid='active-trail:route:entity.node.canonical:route_parameters:a:1:{s:4:\"node\";s:1:\"1\";}'"

You will see something like this:

a:12:{s:4:"main";a:1:{s:0:"";s:0:"";}s:7:"account";a:1:{s:0:"";s:0:"";}i:0;a:1:{s:0:"";s:0:"";}i:1;a:2:{s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:0:"";s:0:"";}i:2;a:1:{s:0:"";s:0:"";}i:3;a:2:{s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:0:"";s:0:"";}i:4;a:1:{s:0:"";s:0:"";}i:5;a:2:{s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:0:"";s:0:"";}i:6;a:1:{s:0:"";s:0:"";}i:7;a:2:{s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:0:"";s:0:"";}i:8;a:1:{s:0:"";s:0:"";}i:9;a:2:{s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:54:"menu_link_content:e7935b9c-1992-4752-931c-e017efba0be0";s:0:"";s:0:"";}}

Note that the same values were copied over and over again. Also notice that the entry for i:4 has changed to
i:4;a:1:{s:0:"";s:0:"";} (i.e. an empty array), which means the current page is not in active trail of the menu (which causes the menu block to be hidden instead of visible).
11. clear the cache (drush cr or drush cc bin menu dynamic_page_cache page)
12. visit /node/1 and check that link 4.1 (from menu 4) shows again on that page

Proposed resolution

Apply provided patch (which changes MenuActiveTrail.php to use set method instead of changing directly the storage property).

Remaining tasks

Test patch

User interface changes

None

API changes

None

Data model changes

None

Release notes snippet

Fix cache_menu bug affecting menus with numeric machine names

file_validate_image_resolution does not update file size after resizing

Problem/Motivation

When an image operation occurs at file_validate_image_resolution, the file object's filesize is not update until the presave of the file.
All operations between these two steps will get the original file size which could lead to errors.
For example the ClamAV module needs it in its hook_file_validate (see issue https://www.drupal.org/project/clamav/issues/3058018)

Steps to reproduce / test case

  1. Set an image field with max_resolution settings.
  2. Add a xdebug break point at the end of file_validate_image_resolution
  3. Add a xdebug break point in a hook_file_validate()
  4. Upload a file bigger than the max resolution
  5. When the first break point is triggered, compare the values of $file->getSize() (original size) and $image->getFileSize() (resized size)
  6. On the second breakpoint $file->getSize() still display the original size
  7. After image is saved, check that the size is correct in the file_managed table

The root cause of the problem is located here:

 $image = $image_factory->get($file->getFileUri());

, $image and $file are now two different objects, an image one with the correct size set on save and a file one that is not updated after the image object modification.

Proposed resolution

Update the $file->setSize() (or any other metadata) after any file manipulations in file_validate_image_resolution() function.

Remaining tasks

  1. Submit a MR.
  2. Add a new test.

User interface changes

None

API changes

None

Data model changes

None


dialog:aftercreate cannot read properties of null (toolbar.js)

Problem/Motivation

When using the media browser popup, it is possible to trigger

react-dom.min.js?v=16.8.4:121 TypeError: Cannot read properties of null (reading 'style')
    at dialog:aftercreate (toolbar.js?v=10.2.0:211:24)
    at dispatch (jquery.min.js?v=3.7.1:2:40035)
    at v.handle (jquery.min.js?v=3.7.1:2:38006)
    at Object.trigger (jquery.min.js?v=3.7.1:2:70124)
    at jquery.min.js?v=3.7.1:2:70726
    at Function.each (jquery.min.js?v=3.7.1:2:3129)
    at ce.fn.init.each (jquery.min.js?v=3.7.1:2:1594)
    at ce.fn.init.trigger (jquery.min.js?v=3.7.1:2:70701)
    at openDialog (dialog.js?v=10.2.0:83:17)
    at dialog.showModal (dialog.js?v=10.2.0:101:7)

when the media browser is rendered as a modal. For example using a combination of Drupal 10.2, Gutenberg v3 and the media browser can trigger it. (It is possible to trigger this issue from other combinations of modules that use the media library popup dialog ui as well)

Steps to reproduce

To reproduce w/the gutenberg editor (it is reproducible from most ways to get to the media browser dialog popup UI)

Insert an image into the node body in gutenberg, click replace on the image in the gui, and hit open media library.

The console will emit errors like the above and

toolbar.js?v=10.2.0:230 Uncaught TypeError: Cannot read properties of null (reading 'style')
    at dialog:beforeclose (toolbar.js?v=10.2.0:230:24)
    at dispatch (jquery.min.js?v=3.7.1:2:40035)
    at v.handle (jquery.min.js?v=3.7.1:2:38006)
    at Object.trigger (jquery.min.js?v=3.7.1:2:70124)
    at jquery.min.js?v=3.7.1:2:70726
    at Function.each (jquery.min.js?v=3.7.1:2:3129)
    at ce.fn.init.each (jquery.min.js?v=3.7.1:2:1594)
    at ce.fn.init.trigger (jquery.min.js?v=3.7.1:2:70701)
    at Object.closeDialog [as close] (dialog.js?v=10.2.0:87:17)
    at Object.close (dialog.js?v=10.2.0:31:35)

The dialog will be blank and there is no way to insert anything from the media library.

The error occurs because toolbar.js assumes that the toolbar is always part of the scope when the scope is actually limited in the context of the media browser popup dialog (I think...?)

Proposed resolution

The fix is pretty easy -- in toolbar.js check to see that toolbarBar is a thing before trying to manipulate it. There is no error handling if document.getElementByID('toolbar-bar'); returns null in this scope.

Apply the attached patch to work around it.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Provide function to get the administrator role ID

Problem/Motivation

It would be useful to be able to easily get the role ID of the administrator role.

Proposed resolution

This code appears in RoleSettingsForm::buildForm():

    $admin_roles = $this->roleStorage->getQuery()
      ->condition('is_admin', TRUE)
      ->execute();
    $default_value = reset($admin_roles);

I would like a function added that includes this code so it can be re-used.

Remaining tasks

Agree that it should be done. Decided on the name of the new function. Implement the function and use it in RoleSettingsForm::buildForm() and any other places this is used.

User interface changes

None.

API changes

None except the addition of the function.

Data model changes

None.

Release notes snippet

Update to current year in COPYRIGHT.txt

Problem/Motivation

We ship a core/COPYRIGHT.txt file, the current 11.x version of which says:

All Drupal code is Copyright 2001 - 2022 by the original authors.

So we made it all the way through 2023 without anyone noticing the bug. 😅 IANAL, but can we do away with trying to put the current year into this text file? Otherwise, we need to update it, and ideally put something in place to help us remember to do this every year going forward. Maybe a Unit test of the \Drupal object that inspects this text file from the source code, parses the line, checks the 2nd year, and fails if it's !== date('y')?

Steps to reproduce

Proposed resolution

Option A: Remove the current year entirely

Probably needs sign-off from a lawyer?

Option B: Update it for now and add a test to remind us to do so every year going forward

Needs a release manager to approve we want this.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Add validation constraints to dblog.settings

Problem/Motivation

Database log settings have 1 property paths that are not yet validatable:

$ ./vendor/bin/drush config:inspect --filter-keys=dblog.settings --detail --list-constraints  --fields=key,validatability,constraints
➜  🤖 Analyzing…

 ------------------------------------------- ------------- ------------------------------------------ 
  Key                                         Validatable   Validation constraints                    
 ------------------------------------------- ------------- ------------------------------------------ 
  dblog.settings                              75%           ValidKeys: '<infer>'                      
                                                            RequiredKeys: '<infer>'                   
   dblog.settings:                            Validatable   ValidKeys: '<infer>'                      
                                                            RequiredKeys: '<infer>'                   
   dblog.settings:_core                       Validatable   ValidKeys:                                
                                                              - default_config_hash                   
                                                            RequiredKeys: '<infer>'                   
   dblog.settings:_core.default_config_hash   Validatable   NotNull: {  }                             
                                                            Regex: '/^[a-zA-Z0-9\-_]+$/'              
                                                            Length: 43                                
                                                            ↣ PrimitiveType: {  }                     
   dblog.settings:row_limit                   NOT           ⚠️  @todo Add validation constraints here  
 ------------------------------------------- ------------- ------------------------------------------ 

Steps to reproduce

  1. Get a local git clone of Drupal core 11.x.
  2. composer require drupal/config_inspector— or manually install https://www.drupal.org/project/config_inspector/releases/2.1.5 or newer (which supports Drupal 11!)
  3. composer require drush/drush
  4. vendor/bin/drush config:inspect --filter-keys=olivero.settings --detail --list-constraints

Proposed resolution

Add validation constraints to:

  1. row_limit

This requires looking at the existing code and admin UI (if any) to understand which values could be considered valid. Eventually this needs to be reviewed by the relevant subsystem maintainer.

For examples, search *.schema.yml files for the string constraints:😊

Reach out to @borisson_ or @wimleers in the #distributions-and-recipes.

Remaining tasks

  1. row_limit

User interface changes

None.

API changes

None.

Data model changes

More validation 🚀

Release notes snippet

None.

Replace FileUploadHandler::loadByUri() with FileRepositoryInterface::loadByUri()

Problem/Motivation

Follow-up to #3223209: deprecate file_save_data, file_copy and file_move and replace with a service

There was too many changes with FileUploadHandler before 9.3.0 alpha release, so this change has been added as follow up.

\Drupal\file\Upload\FileUploadHandler::loadByUri() with \Drupal\file\FileRepositoryInterface::loadByUri()

Steps to reproduce

NA

Proposed resolution

TBD

Remaining tasks

NA

User interface changes

NA

API changes

Deprecate FileUploadHandler::loadByUri() and replace all usages inside the class with FileRepositoryInterface::loadByUri()

Data model changes

NA

Release notes snippet

NA

OpenTelemetryNodePagePerformanceTest::testNodePageHotCache() is not hot enough

Problem/Motivation

OpenTelemetryNodePagePerformanceTest::testNodePageHotCache() should be testing a page cache hit, but according to traces http://grafana.prod.cluster.tag1.io/d/teMVIdjVz/umami?orgId=1&refresh=30..., it's testing a page cache miss (the last query in the traces is setting the page cache).

I tried to add database query assertions to prove this, but ran into [#/3412556] which results in zero database queries being recorded on page cache hits.

Additionally, after fixing that bug, I'm able to add assertions, but they pass locally and fail on gitlab, so something else is going on on gitlab that's resulting in page cache misses.

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Allow kernel tests to fail or expect logged errors

Problem/Motivation

Sometimes, a system will log an error and carry on functioning, because that's the best thing to do in normal operation.

But in a test, the logged error is invisible, and we might want a test to either a) fail on an error, because something's gone wrong in the test, or b) expect the error, because we want to actually test the conditions that cause an error.

Examples of this include:

- The cron service catches exceptions thrown by hook_cron() implementations, logs them, and then continues as normal
- The queue runner catches exceptions thrown by queue workers, logs them, and then continues as normal

Proposed resolution

Add a way for tests to react to logged errors. It should be up to individual tests:

- whether to opt in to being aware of logs
- to fail a test if a log of specified type, of specified or greater severity, is generated
- to fail a test if a log of specified type is not generated

1. To keep the scope tight, this issue is focused on kernel tests as exceptions in the SUT in functional tests are significantly more complex to handle.
2. We create a new AssertableLogger class and register this as a logger, intead of registering the test itself as a logger, to reduce the surface area of KernelTestBase.
3. We created a LoggingTrait to use in KernelTestBase to facilitate future use in other kinds of tests, like functional tests or contrib ExistingSite tests.
4. We don't throw an expection immediately when a log is generated, as this might be swallowed by th SUT. Instead we store logs and evaluate them at the end of a test's execution.
5. To keep the scope tight, we do not opt in to expecting no logged errors in KernelTestBase, this will be explored in a follow-up issue.

User interface changes

None.

API changes

None.

Data model changes

None.


Media Type links in Media Library modal missing vertical tabs styling in Claro

Problem/Motivation

The feature that allows users to preview media using a modal (a type of pop-up window) is not showing correctly. This could mean that when attempting to preview media files such as images, videos, and audio the expected preview within the modal window is not displaying properly. The vertical tabs look broken in the media library dialog. I have attached a screenshot for better reference.

Steps to reproduce

  1. Install Drupal with standard profile
  2. Enable media and media library modules
  3. Add a media field to Article content type
  4. Set widget for media field on Article form to Media Library
  5. Create new Article
  6. Click Add media
  7. You will see the vertical tabs are broken in the media library modal

Proposed resolution

Restore vertical tabs CSS custom properties to core/themes/claro/css/base/variables.pcss.css from core/themes/claro/css/components/vertical-tabs.pcss.css. See #35

Remaining tasks

Needs review

User interface changes

Before

Image may be NSFW.
Clik here to view.
before

After

Image may be NSFW.
Clik here to view.
after

API changes

Data model changes

Release notes snippet

[policy] Service autowiring - decide approach for multiple service implementations based on the same class and different arguments

Problem/Motivation

As noted in #3325557-10: Enable more service autowiring by adding interface aliases to core modules, maybe we could exploit https://symfony.com/doc/current/service_container/autowiring.html#dealin....

This would help autowiring for cache bins and for loggers like e.g.

  cache.default:
    class: Drupal\Core\Cache\CacheBackendInterface
    tags:
      - { name: cache.bin }
    factory: ['@cache_factory', 'get']
    arguments: [default]
  Drupal\Core\Cache\CacheBackendInterface $cacheDefault: '@cache.default'

Proposed resolution

When there are multiple services that use a same interface/factory but with different arguments to instantiate each,

1) in the services.yaml file indicate an alias that includes the interface and a variable name that can be used to differentiate the service (e.g. $cacheBootstrap or $cacheRender);
2) in the constructors of service classes that depend on those services, use the new variable accordingly to select the proper service.

For example, core's services.yml will change like

--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -244,54 +244,63 @@ services:
       - { name: cache.bin, default_backend: cache.backend.memory }
     factory: ['@cache_factory', 'get']
     arguments: [static]
+  Drupal\Core\Cache\CacheBackendInterface $cacheStatic: '@cache.static'
   cache.bootstrap:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
       - { name: cache.bin, default_backend: cache.backend.chainedfast }
     factory: ['@cache_factory', 'get']
     arguments: [bootstrap]
+  Drupal\Core\Cache\CacheBackendInterface $cacheBootstrap: '@cache.bootstrap'
   cache.config:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
       - { name: cache.bin, default_backend: cache.backend.chainedfast }
     factory: ['@cache_factory', 'get']
     arguments: [config]
+  Drupal\Core\Cache\CacheBackendInterface $cacheConfig: '@cache.config'
   cache.default:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
       - { name: cache.bin }
     factory: ['@cache_factory', 'get']
     arguments: [default]
+  Drupal\Core\Cache\CacheBackendInterface $cacheDefault: '@cache.default'
   cache.entity:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
       - { name: cache.bin }
     factory: ['@cache_factory', 'get']
     arguments: [entity]
+  Drupal\Core\Cache\CacheBackendInterface $cacheEntity: '@cache.entity'
   cache.menu:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
       - { name: cache.bin }
     factory: ['@cache_factory', 'get']
     arguments: [menu]
+  Drupal\Core\Cache\CacheBackendInterface $cacheMenu: '@cache.menu'
   cache.render:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
       - { name: cache.bin }
     factory: ['@cache_factory', 'get']
     arguments: [render]
+  Drupal\Core\Cache\CacheBackendInterface $cacheRender: '@cache.render'
   cache.data:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
       - { name: cache.bin }
     factory: ['@cache_factory', 'get']
     arguments: [data]
+  Drupal\Core\Cache\CacheBackendInterface $cacheData: '@cache.data'
   cache.discovery:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
       - { name: cache.bin, default_backend: cache.backend.chainedfast }
     factory: ['@cache_factory', 'get']
     arguments: [discovery]
+  Drupal\Core\Cache\CacheBackendInterface $cacheDiscovery: '@cache.discovery'
   cache_router_rebuild_subscriber:
     class: Drupal\Core\EventSubscriber\CacheRouterRebuildSubscriber
   page_cache_request_policy:
@@ -472,31 +481,40 @@ services:
   logger.channel.default:
     parent: logger.channel_base
     arguments: ['system']
+  Psr\Log\LoggerInterface $loggerDefault: '@logger.channel.default'
   logger.channel.php:
     parent: logger.channel_base
     arguments: ['php']
+  Psr\Log\LoggerInterface $loggerPhp: '@logger.channel.php'
   logger.channel.image:
     parent: logger.channel_base
     arguments: ['image']
+  Psr\Log\LoggerInterface $loggerImage: '@logger.channel.image'
   logger.channel.cron:
     parent: logger.channel_base
     arguments: ['cron']
+  Psr\Log\LoggerInterface $loggerCron: '@logger.channel.cron'
   logger.channel.file:
     class: Drupal\Core\Logger\LoggerChannel
     factory: ['@logger.factory', 'get']
     arguments: ['file']
+  Psr\Log\LoggerInterface $loggerFile: '@logger.channel.file'
   logger.channel.form:
     parent: logger.channel_base
     arguments: ['form']
+  Psr\Log\LoggerInterface $loggerForm: '@logger.channel.form'
   logger.channel.security:
     parent: logger.channel_base
     arguments: ['security']
+  Psr\Log\LoggerInterface $loggerSecurity: '@logger.channel.security'
   logger.channel.menu:
     parent: logger.channel_base
     arguments: ['menu']
+  Psr\Log\LoggerInterface $loggerMenu: '@logger.channel.menu'
   logger.channel.router:
     parent: logger.channel_base
     arguments: ['router']
+  Psr\Log\LoggerInterface $loggerRouter: '@logger.channel.router'
   logger.log_message_parser:
     class: Drupal\Core\Logger\LogMessageParser
   Drupal\Core\Logger\LogMessageParserInterface: '@logger.log_message_parser'

and an (autowired) service using any of the aliased services will have a constructor like

--- a/core/lib/Drupal/Core/Extension/ModuleHandler.php
+++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php
@@ -107,7 +107,7 @@ class ModuleHandler implements ModuleHandlerInterface {
    * @see \Drupal\Core\DrupalKernel
    * @see \Drupal\Core\CoreServiceProvider
    */
-  public function __construct($root, array $module_list, CacheBackendInterface $cache_backend) {
+  public function __construct($root, array $module_list, CacheBackendInterface $cacheBootstrap) {
     $this->root = $root;
     $this->moduleList = [];
     foreach ($module_list as $name => $module) {
 

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Field storage migration fails on re-import

Problem/Motivation

When running a migration repeatedly, with the "update" option, it should update existing entities, instead of creating new entities.

Unfortunately, when migrating field storage configs from e.g. Drupal 7, the migration fails to find the existing field storage.

This leads to errors like "'field_storage_config' entity with ID 'comment.comment_body' already exists."

The reason is that the ->getEntity method only uses the first part of the config entity id array, when trying to load an existing field storage.

Other destination plugins don't have this problem, because they have their own logic when dealing with the id.

-----

The problem is the reset() in Drupal\migrate\Plugin\migrate\destination\Entity::getEntity():

  protected function getEntity(Row $row, array $old_destination_id_values) {
    $entity_id = reset($old_destination_id_values) ?: $this->getEntityId($row);
    if (!empty($entity_id) && ($entity = $this->storage->load($entity_id))) {

For field storage this will fail, if e.g. $old_destination_id_values === ['comment', 'comment_body'].

Steps to reproduce

Generate migrations to upgrade from a from Drupal 7 site.
(I use migrate_tools and migrate_plus to create them as config entities. But I suppose the same happens if you just use core)

Run the "upgrade_d7_field" (or d7_field) migration repeatedly with --update.

Expected: Field storages are updated on repeated import.
Actual: Errors like "'field_storage_config' entity with ID 'comment.comment_body' already exists."

Proposed resolution

Field storage needs its own logic to deal with the ids or load the old entity.

Remaining tasks

Also review other migrate destinations.
Those that I checked seemed ok, but maybe there are others that are broken like field storage.

User interface changes

API changes

Data model changes

Release notes snippet

Adding links around embedded media through CKEditor might lead to invalid/complex markup when rendered

Problem/Motivation

The Drupal link CKEditor button can be used to add links to embedded media items. However, we can't guarantee that the rendered representation of that media item will be a single HTML element, or one that can be safely wrapped with <a> tags. As a result, editors can create links that have arbitrary HTML elements inside them.

Steps to reproduce:

1) Go to simplytest.me and spin a vanilla install of Drupal 8.8.x. Log in with admin/admin, install the "Media" and "Media Library" modules.
2) Go to /admin/config/content/formats/manage/basic_html?destination=/admin/config/content/formats and drag the "Insert from Media Library" button into the CKEditor toolbar. Enable the "Embed media" filter, and click "Save configuration".
3) Go to /admin/structure/media/manage/image/display and drag the "Authored by" field up into the visible fields area. Click "Save" to save this view display settings.
4) Go to /node/add/article to open the article node form. In the wysiwyg, click the "Insert from Media Library" button. Upload a file into the upload zone, give it an ALT text. Save and insert. At this point you will have something like this:

Image may be NSFW.
Clik here to view.
Image1

5) Click the media item to select it, then click the "Link" button from the CKEditor toolbar. Give it a random URL (such as https://drupal.org)
6) At this point, if you view the source markup you will have something similar to this:

<p>text before media</p>
<a href="https://drupal.org">
<drupal-media data-align="center" data-entity-type="media" data-entity-uuid="94a3ff34-a958-427c-ac51-127259cbc1e2"></drupal-media>
</a>

<p>text after media</p>

7) Fill in the required fields and save the node.
8) Inspect the markup of the node body, you will find something like this:

Image may be NSFW.
Clik here to view.
Image2

Proposed resolution

TBD

Remaining tasks

- Discuss and agree on a reasonable approach
- Implement it
- Review & commit

User interface changes

TBD

API changes

TBD

Data model changes

TBD

Release notes snippet

TBD

Original report from @wrd :

With the progress being made on #2801307: [META] Support WYSIWYG embedding of media entities, I've been playing with the new capabilities and have run into what seems like an important issue for typical users: after embedding some media entities, particularly images, a user will probably expect to be able to link the media to another page. At present, this doesn't really work.

  1. Perform a Standard install.
  2. Enable the Media and Media Library modules.
  3. Edit the Basic HTML editor. Enable the "Embed media" filter, remove the "Image" button, add the "Insert from Media Library" button. <drupal-media data-entity-type data-entity-uuid> is added to the allowed HTML tags automatically.
  4. Create a new Image media entity in the Media Library.
  5. Create a new Basic Page.
  6. Use the "Insert from Media Library" button to embed an image in the body field.

    Image may be NSFW.
    Clik here to view.
    Embed the image entity
  7. Click on the embedded image to select it.
  8. Click the Link button to link the image to a destination. I'll link it to https://drupal.org.

    Image may be NSFW.
    Clik here to view.
    Link the image entity
  9. When done, note that there's no visual indication that the embedded image has been linked to anything.

    Image may be NSFW.
    Clik here to view.
    No indication that this image is wrapped in an a tag
  10. Save and publish the node, and view it.

You'll see that the image is, indeed, linked to https://drupal.org. However, if you inspect the element, the <a> tag has been wrapped around the entire media entity:

<article data-history-node-id="1" role="article" about="/node/1" typeof="schema:WebPage" class="node node--type-page node--view-mode-full clearfix">
  <header>
    <span property="schema:name" content="Test Page 1" class="rdf-meta hidden"></span>
  </header>
  <div class="node__content clearfix">
    <div property="schema:text" class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item">
      <p>This is a test. </p>
      <a href="https://drupal.org">
        <article class="media media--type-image media--view-mode-full">
          <div class="field field--name-field-media-image field--type-image field--label-visually_hidden">
            <div class="field__label visually-hidden">
              Image
            </div>
            <div class="field__item">
              <a href="https://drupal88.dd:8443/sites/default/files/2019-08/pears_1.jpg">
                <img src="/sites/default/files/2019-08/pears_1.jpg" width="640" height="480" alt="Pears" typeof="foaf:Image" />
              </a>
            </div>
          </div>
        </article>
      </a>
      <p>This is only a test. </p>
    </div>  
  </div>
</article>

While we can wrap block elements in <a> tags in HTML5, the result here is problematic. The image itself is (by default) linked to the raw image file, so we've got nested hyperlinks, which AFAIK isn't permitted in HTML5. And browsers (Safari, anyway) will ultimately render this as multiple separate <a> tags, most of them empty or wrapping whitespace.

It seems like the expected default behavior for a typical user would be that after using the Link button, there would be some kind of visual indication in CKEditor that the entity is linked, and the rendered page would simply have the image linked to the destination specified using the Link button.

However, to complicate matters, we have to anticipate the possibility that the media entity being embedded is more complex than just an image. For example, what if I have a custom media type that looks like the Image type, but has a few more fields:

  • Copyright (date field)
  • Location (geolocation field)
  • Photographer (entity reference field)

...and a default view mode that displays the image, the copyright date, the location, and a link to the Photographer entity.

If I embed this entity, and then use the link button, the desired behavior is probably for the image to link to the URL entered using the link button, while leaving the link to the Photographer node untouched so that it still works.

I'm not sure if this more complex case can be handled in any reasonably generalized fashion that'll work for most users. However, the more basic behavior of simply linking an embedded image entity should probably work out of the box, shouldn't it?

I hope this is useful! Let me know if I can clarify anything.

Cannot configure CKEditor 5 if site's themes are configured in a particular way

Problem/Motivation

I updated a site to Drupal 10. But after the upgrade, I am unable to configure CKEditor5 as the editor for any text format. I select it in the dropdown list (None, CKEditor5), and when I do so, the editing display does not appear to select the editing icons. If I click OK, I get a message that I must configure the editor, and when I do, CKEditor5 is unselected again, and I am back to the starting point.

Location 	https://mysite/admin/config/content/formats/manage/basic_html?_wrapper_format=drupal_ajax&ajax_form=1
Referrer 	https://mysite/admin/config/content/formats/manage/basic_html
Message 	TypeError: Drupal\ckeditor5\CKEditor5StylesheetsMessage::checkForStylesheetsEquivalent(): Argument #1 ($theme_info) must be of type array, null given, called in /srv/www/htdocs/ssl2/sn/web/core/modules/ckeditor5/src/CKEditor5StylesheetsMessage.php on line 79 in Drupal\ckeditor5\CKEditor5StylesheetsMessage->checkForStylesheetsEquivalent() (line 109 of /srv/www/htdocs/ssl2/sn/web/core/modules/ckeditor5/src/CKEditor5StylesheetsMessage.php).
Severity 	Error

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Viewing all 294476 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>