The second alpha release of Drupal 9 caused much fewer issues in my site. Mainly, I only had to deal with a single, really specific problem across all the contrib modules I use.

Earlier, in 2017 and 2018, some of Drupal’s security releases have shown that the render system needs to be stricter about what it allows to be called by a callback. And almost a year ago, we also got a solution: https://dgo.to/2966725. If we check Drupal 9.0.0-alpha2 release notes, it highlights only one major thing (imho):

In this alpha release all previously deprecated APIs have been removed. This means that 9.0.0-alpha2 essentially has the same backend API that 9.0.0 will have, so module developers and site owners can confidently test their modules with 9.0.0-alpha2.

For my site, this meant that I had to patch some contributed modules which missed tracking this change record.

I applied the following pattern for every single procedural #pre_render callback:

  1. New PHP class in the module’s src folder with namespace Drupal\[module_name]; class name: ModuleNamePreRender.
  2. Class implements \Drupal\Core\Security\TrustedCallbackInterface.
  3. The new class mirrors the procedural module_name_pre_render() function as an appropriate public static moduleNamePreRender() class method, which reuses the pre-existing procedural function.

For instance, in the case of Admin Toolbar, the pre render function that I had to fix was admin_toolbar_prerender_toolbar_administration_tray(). So I changed admin_toolbar.module:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
diff --git a/admin_toolbar.module b/admin_toolbar.module
index 3c5e81b..918c622 100755
--- a/admin_toolbar.module
+++ b/admin_toolbar.module
@@ -10,12 +10,13 @@ use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Url;
 use Drupal\Component\Utility\Html;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\admin_toolbar\AdminToolbarPreRender;
 
 /**
  * Implements hook_toolbar_alter().
  */
 function admin_toolbar_toolbar_alter(&$items) {
-  $items['administration']['tray']['toolbar_administration']['#pre_render'] = ['admin_toolbar_prerender_toolbar_administration_tray'];
+  $items['administration']['tray']['toolbar_administration']['#pre_render'] = [[AdminToolbarPreRender::class, 'adminToolbarPrerenderToolbarAdministrationTray']];
   $items['administration']['#attached']['library'][] = 'admin_toolbar/toolbar.tree';
   $admin_toolbar_tools = \Drupal::service('module_handler')
     ->moduleExists('admin_toolbar_tools');

…and I added a new shim class AdminToolbarPreRender:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php

namespace Drupal\admin_toolbar;

use Drupal\Core\Security\TrustedCallbackInterface;

class AdminToolbarPreRender implements TrustedCallbackInterface {

  /**
   * Pre-renders the toolbar's administration tray.
   *
   * @param array $element
   *   A renderable array.
   *
   * @return array
   *   The updated renderable array.
   */
  public static function adminToolbarPrerenderToolbarAdministrationTray(array $element) {
    $element = admin_toolbar_prerender_toolbar_administration_tray($element);
    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public static function trustedCallbacks() {
    return ['adminToolbarPrerenderToolbarAdministrationTray'];
  }

}

I also had to apply the pattern above for:

  • Admin Toolbar
  • CAPTCHA
  • Field Group
  • Token

Simple cases

These where the modules where I only had to add the missing TrustedCallbackInterface to a preexisting PHP class and define the trustable callbacks:

  • Bootstrap (8.x-3.x)
  • Entity Browser
  • Entity Embed
  • For the render elements of Field Group
  • Page Manager
  • Slick Carousel

For example, the patch I added to Entity Embed was this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
diff --git a/src/Plugin/entity_embed/EntityEmbedDisplay/EntityReferenceFieldFormatter.php b/src/Plugin/entity_embed/EntityEmbedDisplay/EntityReferenceFieldFormatter.php
index a8f8ffe..be58992 100644
--- a/src/Plugin/entity_embed/EntityEmbedDisplay/EntityReferenceFieldFormatter.php
+++ b/src/Plugin/entity_embed/EntityEmbedDisplay/EntityReferenceFieldFormatter.php
@@ -11,6 +11,7 @@ use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\TypedData\TypedDataManager;
 use Drupal\entity_embed\EntityEmbedDisplay\FieldFormatterEntityEmbedDisplayBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Security\TrustedCallbackInterface;
 
 /**
  * Entity Embed Display reusing entity reference field formatters.
@@ -25,7 +26,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *   supports_image_alt_and_title = TRUE
  * )
  */
-class EntityReferenceFieldFormatter extends FieldFormatterEntityEmbedDisplayBase {
+class EntityReferenceFieldFormatter extends FieldFormatterEntityEmbedDisplayBase implements TrustedCallbackInterface {
 
   /**
    * The configuration factory.
@@ -194,4 +195,11 @@ class EntityReferenceFieldFormatter extends FieldFormatterEntityEmbedDisplayBase
     return $build;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function trustedCallbacks() {
+    return ['disableContextualLinks', 'disableQuickEdit'];
+  }
+
 }

Missing config_export definition in @ConfigEntityType annotations

I have two custom config entity types on this page which were using their config schema as a fallback for their missing config_export definition. This was also a deprecated feature and was removed in this second alpha release.