Files
cms-gov/app/Services/ContentRenderer.php

146 lines
5.1 KiB
PHP

<?php
namespace App\Services;
/**
* Content Renderer Service
* Converts Editor.js JSON blocks to HTML
*/
class ContentRenderer
{
/**
* Render Editor.js blocks to HTML
*
* @param array $blocks Editor.js blocks array
* @return string HTML content
*/
public static function renderEditorJsToHtml(array $blocks): string
{
$html = '';
foreach ($blocks as $block) {
if (!isset($block['type']) || !isset($block['data'])) {
continue; // Skip invalid blocks
}
switch ($block['type']) {
case 'paragraph':
$html .= '<p>' . self::escapeHtml($block['data']['text'] ?? '') . '</p>';
break;
case 'header':
$level = $block['data']['level'] ?? 2;
$text = self::escapeHtml($block['data']['text'] ?? '');
$html .= "<h{$level}>{$text}</h{$level}>";
break;
case 'list':
$style = $block['data']['style'] ?? 'unordered';
$listTag = ($style === 'ordered') ? 'ol' : 'ul';
$html .= "<{$listTag}>";
if (isset($block['data']['items']) && is_array($block['data']['items'])) {
foreach ($block['data']['items'] as $item) {
$html .= '<li>' . self::escapeHtml($item) . '</li>';
}
}
$html .= "</{$listTag}>";
break;
case 'quote':
$text = self::escapeHtml($block['data']['text'] ?? '');
$caption = isset($block['data']['caption']) ? self::escapeHtml($block['data']['caption']) : '';
$html .= '<blockquote><p>' . $text . '</p>';
if ($caption) {
$html .= '<cite>' . $caption . '</cite>';
}
$html .= '</blockquote>';
break;
case 'code':
$code = self::escapeHtml($block['data']['code'] ?? '');
$html .= '<pre><code>' . $code . '</code></pre>';
break;
case 'table':
$html .= '<table><tbody>';
if (isset($block['data']['content']) && is_array($block['data']['content'])) {
foreach ($block['data']['content'] as $row) {
if (is_array($row)) {
$html .= '<tr>';
foreach ($row as $cell) {
$html .= '<td>' . self::escapeHtml($cell) . '</td>';
}
$html .= '</tr>';
}
}
}
$html .= '</tbody></table>';
break;
case 'delimiter':
$html .= '<hr>';
break;
case 'image':
$url = self::escapeHtml($block['data']['file']['url'] ?? '');
$caption = isset($block['data']['caption']) ? self::escapeHtml($block['data']['caption']) : '';
if ($url) {
$html .= '<figure><img src="' . $url . '" alt="' . $caption . '">';
if ($caption) {
$html .= '<figcaption>' . $caption . '</figcaption>';
}
$html .= '</figure>';
}
break;
case 'linkTool':
$link = self::escapeHtml($block['data']['link'] ?? '');
$title = isset($block['data']['meta']['title'])
? self::escapeHtml($block['data']['meta']['title'])
: $link;
if ($link) {
$html .= '<div class="link-tool"><a href="' . $link . '" target="_blank" rel="noopener">' . $title . '</a></div>';
}
break;
default:
// Unknown block type - ignore safely
log_message('debug', 'Unknown Editor.js block type: ' . ($block['type'] ?? 'unknown'));
break;
}
}
return $html;
}
/**
* Escape HTML special characters
*
* @param string $text
* @return string
*/
protected static function escapeHtml(string $text): string
{
return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
}
/**
* Extract excerpt from blocks (first paragraph)
*
* @param array $blocks
* @param int $length
* @return string
*/
public static function extractExcerpt(array $blocks, int $length = 160): string
{
foreach ($blocks as $block) {
if ($block['type'] === 'paragraph' && isset($block['data']['text'])) {
$text = strip_tags($block['data']['text']);
return mb_substr($text, 0, $length);
}
}
return '';
}
}