Fix the problem of showing 500 error while containing special chars in excerpt (#5263)

#### What type of PR is this?

/kind bug
/area core
/milestone 2.12.0

#### What this PR does / why we need it:

This PR refactors building html meta by using `modelFactory#createStandaloneElementTag`.

See https://github.com/halo-dev/halo/issues/4755 for more.

#### Which issue(s) this PR fixes:

Fixes https://github.com/halo-dev/halo/issues/4755

#### Special notes for your reviewer:

Validate via <https://github.com/halo-dev/halo/issues/4755#issuecomment-1776391345>.

#### Does this PR introduce a user-facing change?

```release-note
修复摘要中包含特殊字符导致无法解析页面的问题
```
pull/5274/head^2
John Niang 2024-01-29 16:58:48 +08:00 committed by GitHub
parent 9137d50a31
commit 47a1aa7631
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 29 additions and 24 deletions

View File

@ -2,11 +2,13 @@ package run.halo.app.theme.dialect;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.thymeleaf.model.AttributeValueQuotes.DOUBLE;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.Order;
@ -15,6 +17,7 @@ import org.springframework.web.util.HtmlUtils;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.model.IModel;
import org.thymeleaf.model.IModelFactory;
import org.thymeleaf.model.ITemplateEvent;
import org.thymeleaf.processor.element.IElementModelStructureHandler;
import reactor.core.publisher.Mono;
import run.halo.app.theme.DefaultTemplateEnum;
@ -62,11 +65,9 @@ public class ContentTemplateHeadProcessor implements TemplateHeadProcessor {
}
return htmlMetasMono
.doOnNext(htmlMetas -> {
String metaHtml = headMetaBuilder(htmlMetas);
IModelFactory modelFactory = context.getModelFactory();
model.add(modelFactory.createText(metaHtml));
})
.doOnNext(
htmlMetas -> buildMetas(context.getModelFactory(), htmlMetas).forEach(model::add)
)
.then();
}
@ -97,19 +98,12 @@ public class ContentTemplateHeadProcessor implements TemplateHeadProcessor {
String CONTENT = "content";
}
private String headMetaBuilder(List<Map<String, String>> htmlMetas) {
if (htmlMetas == null) {
return StringUtils.EMPTY;
}
StringBuilder sb = new StringBuilder();
for (Map<String, String> htmlMeta : htmlMetas) {
sb.append("<meta");
htmlMeta.forEach((k, v) -> {
sb.append(" ").append(k).append("=\"").append(v).append("\"");
});
sb.append(" />\n");
}
return sb.toString();
private List<ITemplateEvent> buildMetas(IModelFactory modelFactory,
List<Map<String, String>> metas) {
return metas.stream()
.map(metaMap ->
modelFactory.createStandaloneElementTag("meta", metaMap, DOUBLE, false, true)
).collect(Collectors.toList());
}
private boolean isPostTemplate(ITemplateContext context) {

View File

@ -12,6 +12,7 @@ import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.model.IModel;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.model.ITemplateEvent;
import org.thymeleaf.model.IText;
import org.thymeleaf.processor.element.IElementModelStructureHandler;
@ -59,9 +60,19 @@ public class DuplicateMetaTagProcessor implements TemplateHeadProcessor {
IText otherText = context.getModelFactory()
.createText(text);
otherModel.add(new IndexedModel(i, otherText));
} else {
otherModel.add(new IndexedModel(i, templateEvent));
continue;
}
if (templateEvent instanceof IProcessableElementTag tag) {
var indexedModel = new IndexedModel(i, tag);
if ("meta".equals(tag.getElementCompleteName())) {
var attribute = tag.getAttribute("name");
if (attribute != null) {
uniqueMetaTags.put(attribute.getValue(), indexedModel);
continue;
}
}
}
otherModel.add(new IndexedModel(i, templateEvent));
}
otherModel.addAll(uniqueMetaTags.values());

View File

@ -146,8 +146,8 @@ class ContentTemplateHeadProcessorIntegrationTest {
<head>
<meta charset="UTF-8">
<title>Post detail</title>
<meta name="description" content="post-description">
<meta name="keyword" content="postK1,postK2">
<meta name="description" content="post-description">
<meta name="other" content="post-other-meta">
</head>
<body>

View File

@ -174,9 +174,9 @@ class HaloProcessorDialectTest {
<title>Post</title>
<meta name="global-head-test" content="test" />
<meta name="content-head-test" content="test" />
<meta content="post-meta-V1" name="post-meta-V1" />
<meta content="post-meta-V2" name="post-meta-V2" />
<meta name="description" content="" />
<meta content="post-meta-V1" name="post-meta-V1"/>\
<meta content="post-meta-V2" name="post-meta-V2"/>\
<meta name="description" content=""/>\
</head>
<body>
<p>post</p>