-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
/
ContentTemplateHeadProcessor.java
124 lines (113 loc) · 4.75 KB
/
ContentTemplateHeadProcessor.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package run.halo.app.theme.dialect;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.util.HtmlUtils;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.model.IModel;
import org.thymeleaf.model.IModelFactory;
import org.thymeleaf.processor.element.IElementModelStructureHandler;
import reactor.core.publisher.Mono;
import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.PostFinder;
import run.halo.app.theme.finders.SinglePageFinder;
import run.halo.app.theme.router.ModelConst;
/**
* <p>The <code>head</code> html snippet injection processor for content template such as post
* and page.</p>
*
* @author guqing
* @since 2.0.0
*/
@Component
@Order(1)
@AllArgsConstructor
public class ContentTemplateHeadProcessor implements TemplateHeadProcessor {
private static final String POST_NAME_VARIABLE = "name";
private final PostFinder postFinder;
private final SinglePageFinder singlePageFinder;
@Override
public Mono<Void> process(ITemplateContext context, IModel model,
IElementModelStructureHandler structureHandler) {
Mono<String> nameMono = Mono.justOrEmpty((String) context.getVariable(POST_NAME_VARIABLE));
Mono<List<Map<String, String>>> htmlMetasMono = Mono.empty();
if (isPostTemplate(context)) {
htmlMetasMono = nameMono.flatMap(postFinder::getByName)
.map(post -> {
List<Map<String, String>> htmlMetas = post.getSpec().getHtmlMetas();
String excerpt =
post.getStatus() == null ? null : post.getStatus().getExcerpt();
return excerptToMetaDescriptionIfAbsent(htmlMetas, excerpt);
});
} else if (isPageTemplate(context)) {
htmlMetasMono = nameMono.flatMap(singlePageFinder::getByName)
.map(page -> {
List<Map<String, String>> htmlMetas = page.getSpec().getHtmlMetas();
String excerpt =
page.getStatus() == null ? null : page.getStatus().getExcerpt();
return excerptToMetaDescriptionIfAbsent(htmlMetas, excerpt);
});
}
return htmlMetasMono
.doOnNext(htmlMetas -> {
String metaHtml = headMetaBuilder(htmlMetas);
IModelFactory modelFactory = context.getModelFactory();
model.add(modelFactory.createText(metaHtml));
})
.then();
}
static List<Map<String, String>> excerptToMetaDescriptionIfAbsent(
List<Map<String, String>> htmlMetas,
String excerpt) {
String excerptNullSafe = StringUtils.defaultString(excerpt);
final String excerptSafe = HtmlUtils.htmlEscape(excerptNullSafe);
List<Map<String, String>> metas = new ArrayList<>(defaultIfNull(htmlMetas, List.of()));
metas.stream()
.filter(map -> Meta.DESCRIPTION.equals(map.get(Meta.NAME)))
.distinct()
.findFirst()
.ifPresentOrElse(map ->
map.put(Meta.CONTENT, defaultIfBlank(map.get(Meta.CONTENT), excerptSafe)),
() -> {
Map<String, String> map = new HashMap<>();
map.put(Meta.NAME, Meta.DESCRIPTION);
map.put(Meta.CONTENT, excerptSafe);
metas.add(map);
});
return metas;
}
interface Meta {
String DESCRIPTION = "description";
String NAME = "name";
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 boolean isPostTemplate(ITemplateContext context) {
return DefaultTemplateEnum.POST.getValue()
.equals(context.getVariable(ModelConst.TEMPLATE_ID));
}
private boolean isPageTemplate(ITemplateContext context) {
return DefaultTemplateEnum.SINGLE_PAGE.getValue()
.equals(context.getVariable(ModelConst.TEMPLATE_ID));
}
}