|
@@ -1868,19 +1868,11 @@ class ExamPdfExportService
|
|
|
|
|
|
|
|
private function renderKpExplainMarkdown(string $html): string
|
|
private function renderKpExplainMarkdown(string $html): string
|
|
|
{
|
|
{
|
|
|
- if (! class_exists(\Michelf\MarkdownExtra::class)) {
|
|
|
|
|
- return $html;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- $parser = new \Michelf\MarkdownExtra;
|
|
|
|
|
-
|
|
|
|
|
return preg_replace_callback(
|
|
return preg_replace_callback(
|
|
|
'/<div class="kp-markdown-source"[^>]*>([\s\S]*?)<\/div>\s*<div class="kp-markdown-container[^"]*"[^>]*><\/div>/i',
|
|
'/<div class="kp-markdown-source"[^>]*>([\s\S]*?)<\/div>\s*<div class="kp-markdown-container[^"]*"[^>]*><\/div>/i',
|
|
|
- function ($matches) use ($parser) {
|
|
|
|
|
|
|
+ function ($matches) {
|
|
|
$markdown = html_entity_decode(trim($matches[1]), ENT_QUOTES, 'UTF-8');
|
|
$markdown = html_entity_decode(trim($matches[1]), ENT_QUOTES, 'UTF-8');
|
|
|
- [$protectedMarkdown, $mathPlaceholders] = $this->protectLatexBlocksForMarkdown($markdown);
|
|
|
|
|
- $rendered = $parser->transform($protectedMarkdown);
|
|
|
|
|
- $rendered = strtr($rendered, $mathPlaceholders);
|
|
|
|
|
|
|
+ $rendered = $this->renderKpMarkdownContent($markdown);
|
|
|
|
|
|
|
|
return '<div class="kp-markdown-container kp-markdown-content">'.$rendered.'</div>';
|
|
return '<div class="kp-markdown-container kp-markdown-content">'.$rendered.'</div>';
|
|
|
},
|
|
},
|
|
@@ -5047,24 +5039,116 @@ MARKDOWN;
|
|
|
return '';
|
|
return '';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if ($this->looksLikeHtml($content)) {
|
|
|
|
|
|
|
+ if ($this->looksLikeRenderedKpHtml($content)) {
|
|
|
return $content;
|
|
return $content;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (! class_exists(\Michelf\MarkdownExtra::class)) {
|
|
|
|
|
- $safe = htmlspecialchars($content, ENT_QUOTES, 'UTF-8');
|
|
|
|
|
- return '<div class="kp-markdown-container kp-markdown-content">'.nl2br($safe).'</div>';
|
|
|
|
|
|
|
+ if ($this->looksLikeHtml($content) && ! $this->looksLikeMarkdown($content)) {
|
|
|
|
|
+ return $content;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- $parser = new \Michelf\MarkdownExtra;
|
|
|
|
|
$markdown = html_entity_decode($content, ENT_QUOTES, 'UTF-8');
|
|
$markdown = html_entity_decode($content, ENT_QUOTES, 'UTF-8');
|
|
|
- [$protectedMarkdown, $mathPlaceholders] = $this->protectLatexBlocksForMarkdown($markdown);
|
|
|
|
|
- $rendered = $parser->transform($protectedMarkdown);
|
|
|
|
|
- $rendered = strtr($rendered, $mathPlaceholders);
|
|
|
|
|
|
|
+ $rendered = $this->renderKpMarkdownContent($markdown);
|
|
|
|
|
|
|
|
return '<div class="kp-markdown-container kp-markdown-content">'.$rendered.'</div>';
|
|
return '<div class="kp-markdown-container kp-markdown-content">'.$rendered.'</div>';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private function renderKpMarkdownContent(string $markdown): string
|
|
|
|
|
+ {
|
|
|
|
|
+ if (class_exists(\Michelf\MarkdownExtra::class)) {
|
|
|
|
|
+ $parser = new \Michelf\MarkdownExtra;
|
|
|
|
|
+ [$protectedMarkdown, $mathPlaceholders] = $this->protectLatexBlocksForMarkdown($markdown);
|
|
|
|
|
+ $rendered = $parser->transform($protectedMarkdown);
|
|
|
|
|
+
|
|
|
|
|
+ return strtr($rendered, $mathPlaceholders);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $this->renderBasicKpMarkdown($markdown);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function renderBasicKpMarkdown(string $markdown): string
|
|
|
|
|
+ {
|
|
|
|
|
+ $lines = preg_split('/\R/u', trim($markdown));
|
|
|
|
|
+ if ($lines === false) {
|
|
|
|
|
+ $safe = htmlspecialchars($markdown, ENT_QUOTES, 'UTF-8');
|
|
|
|
|
+
|
|
|
|
|
+ return nl2br($safe);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $html = [];
|
|
|
|
|
+ $paragraph = [];
|
|
|
|
|
+ $listType = null;
|
|
|
|
|
+
|
|
|
|
|
+ $flushParagraph = function () use (&$html, &$paragraph): void {
|
|
|
|
|
+ if ($paragraph === []) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $html[] = '<p>'.implode('<br>', $paragraph).'</p>';
|
|
|
|
|
+ $paragraph = [];
|
|
|
|
|
+ };
|
|
|
|
|
+ $closeList = function () use (&$html, &$listType): void {
|
|
|
|
|
+ if ($listType === null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $html[] = "</{$listType}>";
|
|
|
|
|
+ $listType = null;
|
|
|
|
|
+ };
|
|
|
|
|
+ $openList = function (string $type) use (&$html, &$listType, $closeList): void {
|
|
|
|
|
+ if ($listType === $type) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $closeList();
|
|
|
|
|
+ $html[] = "<{$type}>";
|
|
|
|
|
+ $listType = $type;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($lines as $line) {
|
|
|
|
|
+ $line = rtrim($line);
|
|
|
|
|
+ if ($line === '') {
|
|
|
|
|
+ $flushParagraph();
|
|
|
|
|
+ $closeList();
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (preg_match('/^(#{1,6})\s+(.+)$/u', $line, $match)) {
|
|
|
|
|
+ $flushParagraph();
|
|
|
|
|
+ $closeList();
|
|
|
|
|
+ $level = min(6, strlen($match[1]));
|
|
|
|
|
+ $html[] = sprintf('<h%d>%s</h%d>', $level, $this->escapeMarkdownLine($match[2]), $level);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (preg_match('/^\s*[-*]\s+(.+)$/u', $line, $match)) {
|
|
|
|
|
+ $flushParagraph();
|
|
|
|
|
+ $openList('ul');
|
|
|
|
|
+ $html[] = '<li>'.$this->escapeMarkdownLine($match[1]).'</li>';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (preg_match('/^\s*\d+\.\s+(.+)$/u', $line, $match)) {
|
|
|
|
|
+ $flushParagraph();
|
|
|
|
|
+ $openList('ol');
|
|
|
|
|
+ $html[] = '<li>'.$this->escapeMarkdownLine($match[1]).'</li>';
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $paragraph[] = $this->escapeMarkdownLine($line);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $flushParagraph();
|
|
|
|
|
+ $closeList();
|
|
|
|
|
+
|
|
|
|
|
+ return implode("\n", $html);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function escapeMarkdownLine(string $line): string
|
|
|
|
|
+ {
|
|
|
|
|
+ return htmlspecialchars($line, ENT_QUOTES, 'UTF-8');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 保护 Markdown 中的数学块,避免 MarkdownExtra 吃掉 LaTeX 反斜杠
|
|
* 保护 Markdown 中的数学块,避免 MarkdownExtra 吃掉 LaTeX 反斜杠
|
|
|
*/
|
|
*/
|
|
@@ -5086,16 +5170,26 @@ MARKDOWN;
|
|
|
return [$protected ?? $markdown, $placeholders];
|
|
return [$protected ?? $markdown, $placeholders];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private function looksLikeHtml(string $content): bool
|
|
|
|
|
|
|
+ private function looksLikeRenderedKpHtml(string $content): bool
|
|
|
{
|
|
{
|
|
|
if (stripos($content, 'kp-markdown-container') !== false ||
|
|
if (stripos($content, 'kp-markdown-container') !== false ||
|
|
|
stripos($content, 'kp-markdown-content') !== false) {
|
|
stripos($content, 'kp-markdown-content') !== false) {
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function looksLikeHtml(string $content): bool
|
|
|
|
|
+ {
|
|
|
return (bool) preg_match('/<\s*(p|div|h[1-6]|ul|ol|li|table|span|blockquote|pre|code|br)\b/i', $content);
|
|
return (bool) preg_match('/<\s*(p|div|h[1-6]|ul|ol|li|table|span|blockquote|pre|code|br)\b/i', $content);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private function looksLikeMarkdown(string $content): bool
|
|
|
|
|
+ {
|
|
|
|
|
+ return (bool) preg_match('/(^|\R)\s{0,3}#{1,6}\s+\S|(^|\R)\s{0,3}[-*]\s+\S|(^|\R)\s{0,3}\d+\.\s+\S/u', $content);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private function shouldUseDefaultExplanations(): bool
|
|
private function shouldUseDefaultExplanations(): bool
|
|
|
{
|
|
{
|
|
|
if (!Schema::hasTable('knowledge_points')) {
|
|
if (!Schema::hasTable('knowledge_points')) {
|