I'm maintaining a module that I've had a 3rd party dev build for me. I D9 this issue has appeared when using the module that otherwise works in D8 and should be compatible with 8 & 9.
The module creates blocks with event dates pulled from the Songkick service but on D9 pages with a songkick block enabled throws this error:
Drupal\Core\Security\UntrustedCallbackException: Render #pre_render
callbacks must be methods of a class that implements
\Drupal\Core\Security\TrustedCallbackInterface or be an anonymous
function. The callback was _songkick_block_poweredby_prerender. See
https://www.drupal.org/node/2966725 in
Drupal\Core\Render\Renderer->doTrustedCallback() (line 96 of
/var/www/html/web/core/lib/Drupal/Core/Security/DoTrustedCallbackTrait.php).
I'm a mere site builder with limited coding experience so I'm looking for help to fix the issue. The error mentions this page explaining the issue.
The code below from my .module files includes the pre_render in mention.
/**
* Implements hook_block_view_alter().
*/
function songkick_block_content_view_alter(array &$build) {
$id = $build['#block_content']->id();
$block = \Drupal\block_content\Entity\BlockContent::load($id);
$block_type = $block->type[0]->target_id;
if ($block_type == 'songkick_block') {
$build['#pre_render'][] = '_songkick_block_poweredby_prerender';
}
}
/**
* Get a Upcoming & past events API data.
*/
function _songkick_block_poweredby_prerender(array &$build) {
$id = $build['#block_content']->id();
$block = \Drupal\block_content\Entity\BlockContent::load($id);
$block_type = $block->type[0]->target_id;
if ($block_type == 'songkick_block') {
// Get custom Blocks data.
$artis_id = $block->field_artist_id[0]->value;
$upcoming_show = $block->field_display_upcoming_shows[0]->value;
$past_show = $block->field_display_past_shows[0]->value;
$event_details = $block->field_ticket_button[0]->value;
$events_data = [
'event_details' => $event_details,
'upcoming_show' => $upcoming_show,
'past_show' => $past_show
];
$events_data = [
'#theme' => 'songkick_events',
'#events_data' => $events_data
];
$client = \Drupal::httpClient();
$api_key = \Drupal::config('songkick.settings')->get('songkick_key');
$url = 'http://api.songkick.com/api/3.0/artists/';
// Upcoming event data.
if ($upcoming_show == '1') {
$apikey = $url . $artis_id. "/calendar.json" . "?apikey=". $api_key;
$request = $client->get($apikey);
$body = $request->getBody()->getContents();
$AllData = (array)json_decode($body);
// Last page data
$per_page = $AllData['resultsPage']->perPage;
$total_entry = $AllData['resultsPage']->totalEntries;
$page = (int)($total_entry / $per_page);
//$mainData = $AllData['resultsPage']->results->event;
$temp_var = [];
for ($x = $page; $x >= 0; $x--){
$final_api_key = $apikey . "&page=" . $x;
$upcoming_event_request = $client->get($final_api_key);
$upcoming_event_body = $upcoming_event_request->getBody()->getContents();
$UpcomingEventAllData = json_decode($upcoming_event_body);
$mainData = array_reverse($UpcomingEventAllData->resultsPage->results->event);
//$temp_data[] = $mainData;
foreach ($mainData as $value) {
$temp_var[] = $value;
}
}
$upcoming_events_data = [
'#theme' => 'songkick_events',
'#upcoming_event' => $temp_var
];
}
else{
$upcoming_events_data = [
'#theme' => 'songkick_events',
'#upcoming_event' => ''
];
}
// Past event data.
if ($past_show == '1') {
$apikey = $url . $artis_id. "/gigography.json" . "?apikey=". $api_key;
$request = $client->get($apikey);
$body = $request->getBody()->getContents();
$AllData = (array)json_decode($body);
// Last page data
$per_page = $AllData['resultsPage']->perPage;
$total_entry = $AllData['resultsPage']->totalEntries;
$page = (int)($total_entry / $per_page);
$temp_var = [];
for ($x = $page; $x >= 0; $x--){
$final_api_key = $apikey . "&page=" . $x;
$past_event_request = $client->get($final_api_key);
$past_event_body = $past_event_request->getBody()->getContents();
$PastEventAllData = json_decode($past_event_body);
$mainData = array_reverse($PastEventAllData->resultsPage->results->event);
//$temp_data[] = $mainData;
foreach ($mainData as $value) {
$temp_var[] = $value;
}
}
//$final_api_key = $apikey . "&page=" . $page;
// $past_event_request = $client->get($final_api_key);
// $past_event_body = $past_event_request->getBody()->getContents();
// $PastEventAllData = (array)json_decode($past_event_body);
// $mainData = array_reverse($PastEventAllData['resultsPage']->results->event);
$past_events_data = [
'#theme' => 'songkick_events',
'#past_event' => $temp_var
];
}
else{
$past_events_data = [
'#theme' => 'songkick_events',
'#past_event' => ''
];
}
// Merge Upcoming & past evenst data.
$event_data = array_merge($upcoming_events_data,$past_events_data,$events_data);
// Return events data.
return $event_data;
}
}
I've tried following the instructions in this comment and have added a file named SongkickBlockPoweredByViewBuilder.php in the src folder with this code:
<?php
namespace Drupal\Songkick;
use Drupal\Core\Security\TrustedCallbackInterface;
/**
* Provides a trusted callback for the Songkick Poweredby block.
*
*/
class SongkickBlockPoweredByViewBuilder implements TrustedCallbackInterface {
/**
* {@inheritdoc}
*/
public static function trustedCallbacks() {
return ['preRender'];
}
/**
* Sets Songkick - #pre_render callback.
*/
public static function preRender($build) {
$count = $build['content']['#count'];
$build['content']['#count_text'] = \Drupal::translation()->formatPlural($count, '(@count)', '(@count)');
return $build;
}
}
And I've added the following to my .module file:
use Drupal\Songkick\SongkickBlockPoweredByViewBuilder;
/**
* Implements TrustedCallbackInterface
*/
function _songkick_block_poweredby_prerender(array &$build, Drupal\Core\Block\BlockPluginInterface $block) {
$build['#pre_render'][] = [SongkickBlockPoweredByViewBuilder::class, 'preRender'];
}
Then the site throws this fatal error:
Fatal error: Cannot redeclare _songkick_block_poweredby_prerender()
(previously declared in
/var/www/html/web/modules/contrib/songkick/songkick.module:14) in
/var/www/html/web/modules/contrib/songkick/songkick.module on line 51
The fatal error goes away if I remove the declaration in line 14, but the original error then persist. I'm at a loss about how to define the function in line 51 in a way that resolves the issue. Line 51 begins right after this comment in the above excerpt from the .module file:
/*** Get a Upcoming & past events API data. */
Any tips?