The render_block
filter works for this – I just tested on a fresh install. However, you’ll have to use the final HTML, so it gets a bit ugly.
The following code will copy the first <a>
tag and wrap the copy around each <time>
tag in the list.
\add_filter('render_block', function($content, $parsed): string {
// skip other blocks
if ($parsed['blockName'] !== 'core/latest-posts') {
return $content;
}
// skip latest posts that don't display the date
if (empty($parsed['attrs']['displayPostDate']) || !$parsed['attrs']['displayPostDate']) {
return $content;
}
$dom = new \DomDocument();
// parse the HTML, the @ is required because DomDocument
// doesn't know about HTML5's <time>
@$dom->loadHTML($content);
// get each individual post
foreach ($dom->getElementsByTagName('li') as $entry) {
$links = $entry->getElementsByTagName('a');
if ($links->count() === 0) {
continue;
}
foreach ($entry->getElementsByTagName('time') as $time) {
// clone the first <a>
$link = $links->item(0)->cloneNode();
// wrap the <time> around that <a>
$time->parentNode->replaceChild($link, $time);
$link->appendChild($time);
}
}
// $dom->saveHTML() returns false on error
$newContent = $dom->saveHTML();
if ($newContent !== false) {
return $newContent;
}
return $content;
}, 10, 2);
However, this does not alter the output inside the editor. Since the block is purely rendered in React nowadays, you’d have to use some of the editor filters for that.