From f0892b2f4d7326be1003eb94b906b893c921a220 Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Fri, 23 Sep 2022 11:12:13 +0800 Subject: [PATCH] feat: add theme link expression dialect (#2438) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind feature /milestone 2.0 /area core #### What this PR does / why we need it: 允许主题模板在 HTML 或 JavaScript 片段中使用表达式对象获得链接: - `${#theme.assets('/js/main.js'))}` - `${#theme.route('/categories')}` #### Which issue(s) this PR fixes: Fixes #2435 #### Special notes for your reviewer: /cc @halo-dev/sig-halo #### Does this PR introduce a user-facing change? ```release-note None ``` --- .../halo/app/theme/ThemeConfiguration.java | 6 ++ .../run/halo/app/theme/ThemeLinkBuilder.java | 4 +- .../dialect/DefaultLinkExpressionFactory.java | 66 +++++++++++++++++++ .../dialect/LinkExpressionObjectDialect.java | 27 ++++++++ .../LinkExpressionObjectDialectTest.java | 25 +++++++ 5 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 src/main/java/run/halo/app/theme/dialect/DefaultLinkExpressionFactory.java create mode 100644 src/main/java/run/halo/app/theme/dialect/LinkExpressionObjectDialect.java create mode 100644 src/test/java/run/halo/app/theme/dialect/LinkExpressionObjectDialectTest.java diff --git a/src/main/java/run/halo/app/theme/ThemeConfiguration.java b/src/main/java/run/halo/app/theme/ThemeConfiguration.java index d17fad3bc..520d2445f 100644 --- a/src/main/java/run/halo/app/theme/ThemeConfiguration.java +++ b/src/main/java/run/halo/app/theme/ThemeConfiguration.java @@ -14,6 +14,7 @@ import org.springframework.web.reactive.function.server.ServerResponse; import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect; import run.halo.app.infra.properties.HaloProperties; import run.halo.app.infra.utils.FilePathUtils; +import run.halo.app.theme.dialect.LinkExpressionObjectDialect; import run.halo.app.theme.dialect.ThemeJava8TimeDialect; /** @@ -52,4 +53,9 @@ public class ThemeConfiguration { Java8TimeDialect java8TimeDialect() { return new ThemeJava8TimeDialect(); } + + @Bean + LinkExpressionObjectDialect linkExpressionObjectDialect() { + return new LinkExpressionObjectDialect(); + } } diff --git a/src/main/java/run/halo/app/theme/ThemeLinkBuilder.java b/src/main/java/run/halo/app/theme/ThemeLinkBuilder.java index 3ce73c521..cf90abec2 100644 --- a/src/main/java/run/halo/app/theme/ThemeLinkBuilder.java +++ b/src/main/java/run/halo/app/theme/ThemeLinkBuilder.java @@ -11,8 +11,8 @@ import run.halo.app.infra.utils.PathUtils; * @since 2.0.0 */ public class ThemeLinkBuilder extends StandardLinkBuilder { - private static final String THEME_ASSETS_PREFIX = "/assets"; - private static final String THEME_PREVIEW_PREFIX = "/themes"; + public static final String THEME_ASSETS_PREFIX = "/assets"; + public static final String THEME_PREVIEW_PREFIX = "/themes"; private final ThemeContext theme; diff --git a/src/main/java/run/halo/app/theme/dialect/DefaultLinkExpressionFactory.java b/src/main/java/run/halo/app/theme/dialect/DefaultLinkExpressionFactory.java new file mode 100644 index 000000000..23b84807e --- /dev/null +++ b/src/main/java/run/halo/app/theme/dialect/DefaultLinkExpressionFactory.java @@ -0,0 +1,66 @@ +package run.halo.app.theme.dialect; + +import java.util.Set; +import org.thymeleaf.context.IExpressionContext; +import org.thymeleaf.exceptions.TemplateProcessingException; +import org.thymeleaf.expression.IExpressionObjectFactory; +import org.thymeleaf.linkbuilder.ILinkBuilder; +import org.thymeleaf.util.Validate; +import run.halo.app.theme.ThemeLinkBuilder; + +/** + * A default implementation of {@link IExpressionObjectFactory}. + * + * @author guqing + * @since 2.0.0 + */ +public class DefaultLinkExpressionFactory implements IExpressionObjectFactory { + private static final String THEME_EVALUATION_VARIABLE_NAME = "theme"; + + @Override + public Set getAllExpressionObjectNames() { + return Set.of(THEME_EVALUATION_VARIABLE_NAME); + } + + @Override + public Object buildObject(IExpressionContext context, String expressionObjectName) { + if (THEME_EVALUATION_VARIABLE_NAME.equals(expressionObjectName)) { + return new ThemeLinkExpressObject(context); + } + return null; + } + + @Override + public boolean isCacheable(String expressionObjectName) { + return THEME_EVALUATION_VARIABLE_NAME.equals(expressionObjectName); + } + + public static class ThemeLinkExpressObject { + private final ILinkBuilder linkBuilder; + private final IExpressionContext context; + + /** + * Construct an expression object that provides a set of methods to handle link in + * Javascript or HTML through {@link IExpressionContext}. + * + * @param context expression context + */ + public ThemeLinkExpressObject(IExpressionContext context) { + Validate.notNull(context, "Context cannot be null"); + this.context = context; + Set linkBuilders = context.getConfiguration().getLinkBuilders(); + linkBuilder = linkBuilders.stream() + .findFirst() + .orElseThrow(() -> new TemplateProcessingException("Link builder not found")); + } + + public String assets(String path) { + String assetsPath = ThemeLinkBuilder.THEME_ASSETS_PREFIX + path; + return linkBuilder.buildLink(context, assetsPath, null); + } + + public String route(String path) { + return linkBuilder.buildLink(context, path, null); + } + } +} diff --git a/src/main/java/run/halo/app/theme/dialect/LinkExpressionObjectDialect.java b/src/main/java/run/halo/app/theme/dialect/LinkExpressionObjectDialect.java new file mode 100644 index 000000000..839c382f1 --- /dev/null +++ b/src/main/java/run/halo/app/theme/dialect/LinkExpressionObjectDialect.java @@ -0,0 +1,27 @@ +package run.halo.app.theme.dialect; + +import org.thymeleaf.dialect.AbstractDialect; +import org.thymeleaf.dialect.IExpressionObjectDialect; +import org.thymeleaf.expression.IExpressionObjectFactory; + +/** + * An expression object dialect for theme link. + * + * @author guqing + * @since 2.0.0 + */ +public class LinkExpressionObjectDialect extends AbstractDialect implements + IExpressionObjectDialect { + + private static final IExpressionObjectFactory LINK_EXPRESSION_OBJECTS_FACTORY = + new DefaultLinkExpressionFactory(); + + public LinkExpressionObjectDialect() { + super("themeLink"); + } + + @Override + public IExpressionObjectFactory getExpressionObjectFactory() { + return LINK_EXPRESSION_OBJECTS_FACTORY; + } +} diff --git a/src/test/java/run/halo/app/theme/dialect/LinkExpressionObjectDialectTest.java b/src/test/java/run/halo/app/theme/dialect/LinkExpressionObjectDialectTest.java new file mode 100644 index 000000000..9abf68205 --- /dev/null +++ b/src/test/java/run/halo/app/theme/dialect/LinkExpressionObjectDialectTest.java @@ -0,0 +1,25 @@ +package run.halo.app.theme.dialect; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link LinkExpressionObjectDialect}. + * + * @author guqing + * @since 2.0.0 + */ +class LinkExpressionObjectDialectTest { + + private final LinkExpressionObjectDialect linkExpressionObjectDialect = + new LinkExpressionObjectDialect(); + + @Test + void getExpressionObjectFactory() { + assertThat(linkExpressionObjectDialect.getName()) + .isEqualTo("themeLink"); + assertThat(linkExpressionObjectDialect.getExpressionObjectFactory()) + .isInstanceOf(DefaultLinkExpressionFactory.class); + } +} \ No newline at end of file