mirror of https://github.com/halo-dev/halo
feat: add theme link expression dialect (#2438)
#### 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 ```pull/2460/head
parent
d40626b07b
commit
f0892b2f4d
|
@ -14,6 +14,7 @@ import org.springframework.web.reactive.function.server.ServerResponse;
|
||||||
import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
|
import org.thymeleaf.extras.java8time.dialect.Java8TimeDialect;
|
||||||
import run.halo.app.infra.properties.HaloProperties;
|
import run.halo.app.infra.properties.HaloProperties;
|
||||||
import run.halo.app.infra.utils.FilePathUtils;
|
import run.halo.app.infra.utils.FilePathUtils;
|
||||||
|
import run.halo.app.theme.dialect.LinkExpressionObjectDialect;
|
||||||
import run.halo.app.theme.dialect.ThemeJava8TimeDialect;
|
import run.halo.app.theme.dialect.ThemeJava8TimeDialect;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,4 +53,9 @@ public class ThemeConfiguration {
|
||||||
Java8TimeDialect java8TimeDialect() {
|
Java8TimeDialect java8TimeDialect() {
|
||||||
return new ThemeJava8TimeDialect();
|
return new ThemeJava8TimeDialect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
LinkExpressionObjectDialect linkExpressionObjectDialect() {
|
||||||
|
return new LinkExpressionObjectDialect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,8 @@ import run.halo.app.infra.utils.PathUtils;
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
public class ThemeLinkBuilder extends StandardLinkBuilder {
|
public class ThemeLinkBuilder extends StandardLinkBuilder {
|
||||||
private static final String THEME_ASSETS_PREFIX = "/assets";
|
public static final String THEME_ASSETS_PREFIX = "/assets";
|
||||||
private static final String THEME_PREVIEW_PREFIX = "/themes";
|
public static final String THEME_PREVIEW_PREFIX = "/themes";
|
||||||
|
|
||||||
private final ThemeContext theme;
|
private final ThemeContext theme;
|
||||||
|
|
||||||
|
|
|
@ -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<String> 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<ILinkBuilder> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue