Files
laralog/app/Blog/Services/PostContentRenderer.php
T

105 lines
3.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Blog\Services;
use App\Models\Post;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\GithubFlavoredMarkdownExtension;
use League\CommonMark\MarkdownConverter;
class PostContentRenderer
{
private MarkdownConverter $converter;
public function __construct()
{
$environment = new Environment([
'html_input' => 'allow',
'allow_unsafe_links' => true,
'max_nesting_level' => 100,
]);
$environment->addExtension(new CommonMarkCoreExtension);
$environment->addExtension(new GithubFlavoredMarkdownExtension);
$this->converter = new MarkdownConverter($environment);
}
/**
* 将文章内容按格式渲染为 HTML。
* markdown -> HTMLhtml 原样返回。两种格式都会解析附件令牌。
*/
public function render(Post $post): string
{
$content = $post->content_format === 'markdown'
? $this->toHtml($post->content)
: $post->content;
$html = $this->renderShortcodes($content, $post);
// 插件钩子:付费内容过滤、AI 处理等
return app(\App\Blog\Support\PluginManager::class)->applyFilters('post.rendered', $html, $post);
}
public function toHtml(string $markdown): string
{
return $this->converter->convert($markdown)->getContent();
}
/**
* 解析附件引用。
*
* 兼容两种写法:
* - 老 sablog HTML 内容: [attach=xx] / [img=xx]
* - markdown 转换导入: {{attach:xx}} / {{img:xx}}[] 是 markdown 保留字符,转换时改为令牌)
*
* xx 是 sablog attachmentid;找不到时回退到按出现顺序的第 N 个附件。
*/
public function renderShortcodes(string $html, Post $post): string
{
$pattern = '/\[(attach|img)=(\d+)\]|\{\{(attach|img):(\d+)\}\}/';
if (! preg_match_all($pattern, $html, $matches, PREG_SET_ORDER)) {
return $html;
}
$mediaItems = $post->getMedia('attachments');
$byLegacyId = $mediaItems->keyBy(fn ($m) => (int) $m->getCustomProperty('legacy_attachmentid'));
$ordered = $mediaItems->values();
foreach ($matches as $match) {
$full = $match[0];
$type = $match[1] !== '' ? $match[1] : $match[3];
$id = (int) ($match[2] !== '' ? $match[2] : $match[4]);
$media = $byLegacyId->get($id);
if (! $media) {
// 兼容按索引引用的旧写法:第 $id 个附件(从 1 开始)
$media = $ordered->get($id - 1);
}
if (! $media) {
continue;
}
$replacement = $type === 'img'
? sprintf('<img src="%s" alt="%s" loading="lazy" />', $media->getUrl(), e($media->name))
: sprintf('<a href="%s">%s</a>', $media->getUrl(), e($media->file_name));
$html = str_replace($full, $replacement, $html);
}
return $html;
}
/**
* 把老 sablog HTML 里的 [attach=xx]/[img=xx] 换成 markdown 安全令牌,
* 供 HTML -> markdown 转换前调用。
*/
public function toMarkdownSafeTokens(string $html): string
{
return preg_replace('/\[(attach|img)=(\d+)\]/', '{{$1:$2}}', $html);
}
}