mirror of https://github.com/halo-dev/halo
refactor: excerpt as the meta description on the page of post and single page (#3745)
#### What type of PR is this? /kind improvement /area core #### What this PR does / why we need it: 将文章摘要作为 meta description 以优化文章页的 SEO how to test it? 查看文章页和自定义页面的 head 中是否具有 `<meta name="description" content="文章摘要"/>` 标签 #### Which issue(s) this PR fixes: Fixes #2682 #### Does this PR introduce a user-facing change? ```release-note 将文章摘要作为 meta description 以优化文章页的 SEO ```pull/3714/head^2
parent
e4338c111e
commit
d7bfbef149
|
@ -1,5 +1,10 @@
|
||||||
package run.halo.app.theme.dialect;
|
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.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
@ -37,10 +42,20 @@ public class ContentTemplateHeadProcessor implements TemplateHeadProcessor {
|
||||||
Mono<List<Map<String, String>>> htmlMetasMono = Mono.empty();
|
Mono<List<Map<String, String>>> htmlMetasMono = Mono.empty();
|
||||||
if (isPostTemplate(context)) {
|
if (isPostTemplate(context)) {
|
||||||
htmlMetasMono = nameMono.flatMap(postFinder::getByName)
|
htmlMetasMono = nameMono.flatMap(postFinder::getByName)
|
||||||
.map(post -> post.getSpec().getHtmlMetas());
|
.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)) {
|
} else if (isPageTemplate(context)) {
|
||||||
htmlMetasMono = nameMono.flatMap(singlePageFinder::getByName)
|
htmlMetasMono = nameMono.flatMap(singlePageFinder::getByName)
|
||||||
.map(page -> page.getSpec().getHtmlMetas());
|
.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
|
return htmlMetasMono
|
||||||
|
@ -52,6 +67,32 @@ public class ContentTemplateHeadProcessor implements TemplateHeadProcessor {
|
||||||
.then();
|
.then();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static List<Map<String, String>> excerptToMetaDescriptionIfAbsent(
|
||||||
|
List<Map<String, String>> htmlMetas,
|
||||||
|
String excerpt) {
|
||||||
|
final String excerptNullSafe = StringUtils.defaultString(excerpt);
|
||||||
|
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), excerptNullSafe)),
|
||||||
|
() -> {
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map.put(Meta.NAME, Meta.DESCRIPTION);
|
||||||
|
map.put(Meta.CONTENT, excerptNullSafe);
|
||||||
|
metas.add(map);
|
||||||
|
});
|
||||||
|
return metas;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Meta {
|
||||||
|
String DESCRIPTION = "description";
|
||||||
|
String NAME = "name";
|
||||||
|
String CONTENT = "content";
|
||||||
|
}
|
||||||
|
|
||||||
private String headMetaBuilder(List<Map<String, String>> htmlMetas) {
|
private String headMetaBuilder(List<Map<String, String>> htmlMetas) {
|
||||||
if (htmlMetas == null) {
|
if (htmlMetas == null) {
|
||||||
return StringUtils.EMPTY;
|
return StringUtils.EMPTY;
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
package run.halo.app.theme.dialect;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ContentTemplateHeadProcessor}.
|
||||||
|
*
|
||||||
|
* @author guqing
|
||||||
|
* @since 2.5.0
|
||||||
|
*/
|
||||||
|
class ContentTemplateHeadProcessorTest {
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ExcerptToMetaDescriptionTest {
|
||||||
|
@Test
|
||||||
|
void toMetaWhenExcerptIsNull() {
|
||||||
|
List<Map<String, String>> htmlMetas = new ArrayList<>();
|
||||||
|
htmlMetas.add(createMetaMap("keywords", "test"));
|
||||||
|
var result = ContentTemplateHeadProcessor.excerptToMetaDescriptionIfAbsent(htmlMetas,
|
||||||
|
null);
|
||||||
|
assertThat(result).hasSize(2);
|
||||||
|
assertThat(result.get(0)).containsEntry("name", "keywords");
|
||||||
|
assertThat(result.get(1)).containsEntry("name", "description")
|
||||||
|
.containsEntry("content", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toMetaWhenWhenHtmlMetaIsNull() {
|
||||||
|
var result = ContentTemplateHeadProcessor.excerptToMetaDescriptionIfAbsent(null,
|
||||||
|
null);
|
||||||
|
assertThat(result).hasSize(1);
|
||||||
|
assertThat(result.get(0)).containsEntry("name", "description")
|
||||||
|
.containsEntry("content", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toMetaWhenWhenExcerptNotEmpty() {
|
||||||
|
List<Map<String, String>> htmlMetas = new ArrayList<>();
|
||||||
|
htmlMetas.add(createMetaMap("keywords", "test"));
|
||||||
|
var result = ContentTemplateHeadProcessor.excerptToMetaDescriptionIfAbsent(htmlMetas,
|
||||||
|
"test excerpt");
|
||||||
|
assertThat(result).hasSize(2);
|
||||||
|
assertThat(result.get(0)).containsEntry("name", "keywords");
|
||||||
|
assertThat(result.get(1)).containsEntry("name", "description")
|
||||||
|
.containsEntry("content", "test excerpt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toMetaWhenWhenDescriptionExistsAndEmpty() {
|
||||||
|
List<Map<String, String>> htmlMetas = new ArrayList<>();
|
||||||
|
htmlMetas.add(createMetaMap("keywords", "test"));
|
||||||
|
htmlMetas.add(createMetaMap("description", ""));
|
||||||
|
var result = ContentTemplateHeadProcessor.excerptToMetaDescriptionIfAbsent(htmlMetas,
|
||||||
|
"test excerpt");
|
||||||
|
assertThat(result).hasSize(2);
|
||||||
|
assertThat(result.get(0)).containsEntry("name", "keywords");
|
||||||
|
assertThat(result.get(1)).containsEntry("name", "description")
|
||||||
|
.containsEntry("content", "test excerpt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void toMetaWhenWhenDescriptionExistsAndNotEmpty() {
|
||||||
|
List<Map<String, String>> htmlMetas = new ArrayList<>();
|
||||||
|
htmlMetas.add(createMetaMap("keywords", "test"));
|
||||||
|
htmlMetas.add(createMetaMap("description", "test description"));
|
||||||
|
var result = ContentTemplateHeadProcessor.excerptToMetaDescriptionIfAbsent(htmlMetas,
|
||||||
|
"test excerpt");
|
||||||
|
assertThat(result).hasSize(2);
|
||||||
|
assertThat(result.get(0)).containsEntry("name", "keywords");
|
||||||
|
assertThat(result.get(1)).containsEntry("name", "description")
|
||||||
|
.containsEntry("content", "test description");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> createMetaMap(String nameValue, String contentValue) {
|
||||||
|
Map<String, String> metaMap = new HashMap<>();
|
||||||
|
metaMap.put("name", nameValue);
|
||||||
|
metaMap.put("content", contentValue);
|
||||||
|
return metaMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -169,6 +169,7 @@ class HaloProcessorDialectTest {
|
||||||
<title>Post</title>
|
<title>Post</title>
|
||||||
<meta content="post-meta-V1" name="post-meta-V1" />
|
<meta content="post-meta-V1" name="post-meta-V1" />
|
||||||
<meta content="post-meta-V2" name="post-meta-V2" />
|
<meta content="post-meta-V2" name="post-meta-V2" />
|
||||||
|
<meta name="description" content="" />
|
||||||
<meta name="global-head-test" content="test" />
|
<meta name="global-head-test" content="test" />
|
||||||
<meta name="content-head-test" content="test" />
|
<meta name="content-head-test" content="test" />
|
||||||
</head>
|
</head>
|
||||||
|
|
Loading…
Reference in New Issue