From d7bfbef149ba18ebf66b04786a90e57d2e185a1d Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Wed, 12 Apr 2023 20:50:31 +0800 Subject: [PATCH] refactor: excerpt as the meta description on the page of post and single page (#3745) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### 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 中是否具有 `` 标签 #### Which issue(s) this PR fixes: Fixes #2682 #### Does this PR introduce a user-facing change? ```release-note 将文章摘要作为 meta description 以优化文章页的 SEO ``` --- .../dialect/ContentTemplateHeadProcessor.java | 45 +++++++++- .../ContentTemplateHeadProcessorTest.java | 88 +++++++++++++++++++ .../dialect/HaloProcessorDialectTest.java | 1 + 3 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 application/src/test/java/run/halo/app/theme/dialect/ContentTemplateHeadProcessorTest.java diff --git a/application/src/main/java/run/halo/app/theme/dialect/ContentTemplateHeadProcessor.java b/application/src/main/java/run/halo/app/theme/dialect/ContentTemplateHeadProcessor.java index 02af63841..81d776da7 100644 --- a/application/src/main/java/run/halo/app/theme/dialect/ContentTemplateHeadProcessor.java +++ b/application/src/main/java/run/halo/app/theme/dialect/ContentTemplateHeadProcessor.java @@ -1,5 +1,10 @@ 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; @@ -37,10 +42,20 @@ public class ContentTemplateHeadProcessor implements TemplateHeadProcessor { Mono>> htmlMetasMono = Mono.empty(); if (isPostTemplate(context)) { htmlMetasMono = nameMono.flatMap(postFinder::getByName) - .map(post -> post.getSpec().getHtmlMetas()); + .map(post -> { + List> 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 -> page.getSpec().getHtmlMetas()); + .map(page -> { + List> htmlMetas = page.getSpec().getHtmlMetas(); + String excerpt = + page.getStatus() == null ? null : page.getStatus().getExcerpt(); + return excerptToMetaDescriptionIfAbsent(htmlMetas, excerpt); + }); } return htmlMetasMono @@ -52,6 +67,32 @@ public class ContentTemplateHeadProcessor implements TemplateHeadProcessor { .then(); } + static List> excerptToMetaDescriptionIfAbsent( + List> htmlMetas, + String excerpt) { + final String excerptNullSafe = StringUtils.defaultString(excerpt); + List> 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 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> htmlMetas) { if (htmlMetas == null) { return StringUtils.EMPTY; diff --git a/application/src/test/java/run/halo/app/theme/dialect/ContentTemplateHeadProcessorTest.java b/application/src/test/java/run/halo/app/theme/dialect/ContentTemplateHeadProcessorTest.java new file mode 100644 index 000000000..837e19b1f --- /dev/null +++ b/application/src/test/java/run/halo/app/theme/dialect/ContentTemplateHeadProcessorTest.java @@ -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> 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> 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> 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> 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 createMetaMap(String nameValue, String contentValue) { + Map metaMap = new HashMap<>(); + metaMap.put("name", nameValue); + metaMap.put("content", contentValue); + return metaMap; + } + } +} diff --git a/application/src/test/java/run/halo/app/theme/dialect/HaloProcessorDialectTest.java b/application/src/test/java/run/halo/app/theme/dialect/HaloProcessorDialectTest.java index 30ea61275..872454c2a 100644 --- a/application/src/test/java/run/halo/app/theme/dialect/HaloProcessorDialectTest.java +++ b/application/src/test/java/run/halo/app/theme/dialect/HaloProcessorDialectTest.java @@ -169,6 +169,7 @@ class HaloProcessorDialectTest { Post +