mirror of https://github.com/halo-dev/halo
feat: add supports for provide theme templates in plugin class path (#4862)
* feat: add supports for provide theme templates in plugin class path --------- Co-authored-by: Ryan Wang <i@ryanc.cc>pull/4942/head^2
parent
f5dcb5d925
commit
f659a3279e
|
@ -0,0 +1,45 @@
|
|||
package run.halo.app.theme;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* <p>The {@link TemplateNameResolver} is used to resolve template name.</p>
|
||||
* <code>Halo</code> has a theme mechanism, template files are provided by different themes, so
|
||||
* we need a method to determine whether the template file exists in the activated theme and if
|
||||
* it does not exist, provide a default template name.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public interface TemplateNameResolver {
|
||||
|
||||
/**
|
||||
* Resolve template name if exists or default template name in classpath.
|
||||
*
|
||||
* @param exchange exchange to resolve theme to use
|
||||
* @param name template
|
||||
* @return template name if exists or default template name in classpath
|
||||
*/
|
||||
Mono<String> resolveTemplateNameOrDefault(ServerWebExchange exchange, String name);
|
||||
|
||||
/**
|
||||
* Resolve template name if exists or default template given.
|
||||
*
|
||||
* @param exchange exchange to resolve theme to use
|
||||
* @param name template name
|
||||
* @param defaultName default template name to use if given template name not exists
|
||||
* @return template name if exists or default template name given
|
||||
*/
|
||||
Mono<String> resolveTemplateNameOrDefault(ServerWebExchange exchange, String name,
|
||||
String defaultName);
|
||||
|
||||
/**
|
||||
* Determine whether the template file exists in the current theme.
|
||||
*
|
||||
* @param exchange exchange to resolve theme to use
|
||||
* @param name template name
|
||||
* @return <code>true</code> if the template file exists in the current theme, false otherwise
|
||||
*/
|
||||
Mono<Boolean> isTemplateAvailableInTheme(ServerWebExchange exchange, String name);
|
||||
}
|
|
@ -26,6 +26,8 @@ import org.springframework.util.StopWatch;
|
|||
import reactor.core.Exceptions;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.infra.properties.HaloProperties;
|
||||
import run.halo.app.theme.DefaultTemplateNameResolver;
|
||||
import run.halo.app.theme.DefaultViewNameResolver;
|
||||
|
||||
/**
|
||||
* Plugin application initializer will create plugin application context by plugin id and
|
||||
|
@ -93,9 +95,14 @@ public class PluginApplicationInitializer {
|
|||
pluginApplicationContext.registerBean(AggregatedRouterFunction.class);
|
||||
|
||||
beanFactory.registerSingleton("pluginContext", createPluginContext(plugin));
|
||||
|
||||
// TODO deprecated
|
||||
beanFactory.registerSingleton("pluginWrapper", haloPluginManager.getPlugin(pluginId));
|
||||
|
||||
beanFactory.registerSingleton("templateNameResolver",
|
||||
new DefaultTemplateNameResolver(
|
||||
rootApplicationContext.getBean(DefaultViewNameResolver.class),
|
||||
pluginApplicationContext));
|
||||
populateSettingFetcher(pluginId, beanFactory);
|
||||
|
||||
log.debug("Total millis: {} ms -> {}", stopWatch.getTotalTimeMillis(),
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package run.halo.app.theme;
|
||||
|
||||
import static run.halo.app.plugin.PluginConst.SYSTEM_PLUGIN_NAME;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.plugin.PluginApplicationContext;
|
||||
|
||||
/**
|
||||
* A default implementation of {@link TemplateNameResolver}, It will be provided for plugins to
|
||||
* resolve template name.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public class DefaultTemplateNameResolver implements TemplateNameResolver {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
private final ViewNameResolver viewNameResolver;
|
||||
|
||||
public DefaultTemplateNameResolver(ViewNameResolver viewNameResolver,
|
||||
ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.viewNameResolver = viewNameResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> resolveTemplateNameOrDefault(ServerWebExchange exchange, String name) {
|
||||
if (applicationContext instanceof PluginApplicationContext pluginApplicationContext) {
|
||||
var pluginName = pluginApplicationContext.getPluginId();
|
||||
return this.resolveTemplateNameOrDefault(exchange, name,
|
||||
pluginClassPathTemplate(pluginName, name));
|
||||
}
|
||||
return resolveTemplateNameOrDefault(exchange, name,
|
||||
pluginClassPathTemplate(SYSTEM_PLUGIN_NAME, name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> resolveTemplateNameOrDefault(ServerWebExchange exchange, String name,
|
||||
String defaultName) {
|
||||
return viewNameResolver.resolveViewNameOrDefault(exchange, name, defaultName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> isTemplateAvailableInTheme(ServerWebExchange exchange, String name) {
|
||||
return this.resolveTemplateNameOrDefault(exchange, name, "")
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.hasElement();
|
||||
}
|
||||
|
||||
String pluginClassPathTemplate(String pluginName, String templateName) {
|
||||
return "plugin:" + pluginName + ":" + templateName;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package run.halo.app.theme.router;
|
||||
package run.halo.app.theme;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
@ -7,18 +7,18 @@ import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties;
|
|||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.theme.ThemeResolver;
|
||||
|
||||
/**
|
||||
* The {@link ViewNameResolver} is used to resolve view name.
|
||||
* The {@link DefaultViewNameResolver} is used to resolve view name.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class ViewNameResolver {
|
||||
public class DefaultViewNameResolver implements ViewNameResolver {
|
||||
private static final String TEMPLATES = "templates";
|
||||
private final ThemeResolver themeResolver;
|
||||
private final ThymeleafProperties thymeleafProperties;
|
||||
|
@ -27,12 +27,13 @@ public class ViewNameResolver {
|
|||
* Resolves view name.
|
||||
* If the {@param #name} cannot be resolved to the view, the {@param #defaultName} is returned.
|
||||
*/
|
||||
public Mono<String> resolveViewNameOrDefault(ServerRequest request, String name,
|
||||
@Override
|
||||
public Mono<String> resolveViewNameOrDefault(ServerWebExchange exchange, String name,
|
||||
String defaultName) {
|
||||
if (StringUtils.isBlank(name)) {
|
||||
return Mono.justOrEmpty(defaultName);
|
||||
}
|
||||
return themeResolver.getTheme(request.exchange())
|
||||
return themeResolver.getTheme(exchange)
|
||||
.mapNotNull(themeContext -> {
|
||||
String templateResourceName = computeResourceName(name);
|
||||
var resourcePath = themeContext.getPath()
|
||||
|
@ -43,6 +44,12 @@ public class ViewNameResolver {
|
|||
.switchIfEmpty(Mono.justOrEmpty(defaultName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> resolveViewNameOrDefault(ServerRequest request, String name,
|
||||
String defaultName) {
|
||||
return resolveViewNameOrDefault(request.exchange(), name, defaultName);
|
||||
}
|
||||
|
||||
String computeResourceName(String name) {
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
return StringUtils.endsWith(name, thymeleafProperties.getSuffix())
|
|
@ -2,6 +2,7 @@ package run.halo.app.theme;
|
|||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.nio.file.Path;
|
||||
import lombok.NonNull;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -17,8 +18,10 @@ import org.thymeleaf.templateresolver.ITemplateResolver;
|
|||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.infra.ExternalUrlSupplier;
|
||||
import run.halo.app.infra.exception.NotFoundException;
|
||||
import run.halo.app.plugin.HaloPluginManager;
|
||||
import run.halo.app.theme.dialect.HaloProcessorDialect;
|
||||
import run.halo.app.theme.engine.HaloTemplateEngine;
|
||||
import run.halo.app.theme.engine.PluginClassloaderTemplateResolver;
|
||||
import run.halo.app.theme.message.ThemeMessageResolver;
|
||||
|
||||
/**
|
||||
|
@ -45,6 +48,8 @@ public class TemplateEngineManager {
|
|||
|
||||
private final ExternalUrlSupplier externalUrlSupplier;
|
||||
|
||||
private final HaloPluginManager haloPluginManager;
|
||||
|
||||
private final ObjectProvider<ITemplateResolver> templateResolvers;
|
||||
|
||||
private final ObjectProvider<IDialect> dialects;
|
||||
|
@ -53,10 +58,11 @@ public class TemplateEngineManager {
|
|||
|
||||
public TemplateEngineManager(ThymeleafProperties thymeleafProperties,
|
||||
ExternalUrlSupplier externalUrlSupplier,
|
||||
ObjectProvider<ITemplateResolver> templateResolvers,
|
||||
HaloPluginManager haloPluginManager, ObjectProvider<ITemplateResolver> templateResolvers,
|
||||
ObjectProvider<IDialect> dialects, ThemeResolver themeResolver) {
|
||||
this.thymeleafProperties = thymeleafProperties;
|
||||
this.externalUrlSupplier = externalUrlSupplier;
|
||||
this.haloPluginManager = haloPluginManager;
|
||||
this.templateResolvers = templateResolvers;
|
||||
this.dialects = dialects;
|
||||
this.themeResolver = themeResolver;
|
||||
|
@ -119,6 +125,8 @@ public class TemplateEngineManager {
|
|||
var mainResolver = haloTemplateResolver();
|
||||
mainResolver.setPrefix(cacheKey.context().getPath().resolve("templates") + "/");
|
||||
engine.addTemplateResolver(mainResolver);
|
||||
var pluginTemplateResolver = createPluginClassloaderTemplateResolver();
|
||||
engine.addTemplateResolver(pluginTemplateResolver);
|
||||
// replace StandardDialect with SpringStandardDialect
|
||||
engine.setDialect(new SpringStandardDialect() {
|
||||
@Override
|
||||
|
@ -134,6 +142,19 @@ public class TemplateEngineManager {
|
|||
return engine;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private PluginClassloaderTemplateResolver createPluginClassloaderTemplateResolver() {
|
||||
var pluginTemplateResolver = new PluginClassloaderTemplateResolver(haloPluginManager);
|
||||
pluginTemplateResolver.setPrefix(thymeleafProperties.getPrefix());
|
||||
pluginTemplateResolver.setSuffix(thymeleafProperties.getSuffix());
|
||||
pluginTemplateResolver.setTemplateMode(thymeleafProperties.getMode());
|
||||
pluginTemplateResolver.setOrder(1);
|
||||
if (thymeleafProperties.getEncoding() != null) {
|
||||
pluginTemplateResolver.setCharacterEncoding(thymeleafProperties.getEncoding().name());
|
||||
}
|
||||
return pluginTemplateResolver;
|
||||
}
|
||||
|
||||
FileTemplateResolver haloTemplateResolver() {
|
||||
final var resolver = new FileTemplateResolver();
|
||||
resolver.setTemplateMode(thymeleafProperties.getMode());
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package run.halo.app.theme;
|
||||
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* The {@link ViewNameResolver} is used to resolve view name if the view name cannot be resolved
|
||||
* to the view, the default view name is returned.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.10.2
|
||||
*/
|
||||
public interface ViewNameResolver {
|
||||
Mono<String> resolveViewNameOrDefault(ServerWebExchange exchange, String name,
|
||||
String defaultName);
|
||||
|
||||
Mono<String> resolveViewNameOrDefault(ServerRequest request, String name,
|
||||
String defaultName);
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package run.halo.app.theme.engine;
|
||||
|
||||
import static run.halo.app.plugin.PluginConst.SYSTEM_PLUGIN_NAME;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.pf4j.PluginState;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.thymeleaf.IEngineConfiguration;
|
||||
import org.thymeleaf.spring6.templateresource.SpringResourceTemplateResource;
|
||||
import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver;
|
||||
import org.thymeleaf.templateresource.ITemplateResource;
|
||||
import run.halo.app.plugin.HaloPluginManager;
|
||||
|
||||
/**
|
||||
* Plugin classloader template resolver to resolve template by plugin classloader.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.11.0
|
||||
*/
|
||||
public class PluginClassloaderTemplateResolver extends AbstractConfigurableTemplateResolver {
|
||||
|
||||
private final HaloPluginManager haloPluginManager;
|
||||
static final Pattern PLUGIN_TEMPLATE_PATTERN =
|
||||
Pattern.compile("plugin:([A-Za-z0-9\\-.]+):(.+)");
|
||||
|
||||
/**
|
||||
* Create a new plugin classloader template resolver, not cacheable.
|
||||
*
|
||||
* @param haloPluginManager plugin manager must not be null
|
||||
*/
|
||||
public PluginClassloaderTemplateResolver(HaloPluginManager haloPluginManager) {
|
||||
super();
|
||||
this.haloPluginManager = haloPluginManager;
|
||||
setCacheable(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ITemplateResource computeTemplateResource(
|
||||
final IEngineConfiguration configuration, final String ownerTemplate, final String template,
|
||||
final String resourceName, final String characterEncoding,
|
||||
final Map<String, Object> templateResolutionAttributes) {
|
||||
var matchResult = matchPluginTemplate(ownerTemplate, template);
|
||||
if (!matchResult.matches()) {
|
||||
return null;
|
||||
}
|
||||
String pluginName = matchResult.pluginName();
|
||||
var classloader = getClassloaderByPlugin(pluginName);
|
||||
if (classloader == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var templateName = matchResult.templateName();
|
||||
var ownerTemplateName = matchResult.ownerTemplateName();
|
||||
|
||||
String handledResourceName = computeResourceName(configuration, ownerTemplateName,
|
||||
templateName, getPrefix(), getSuffix(), getForceSuffix(), getTemplateAliases(),
|
||||
templateResolutionAttributes);
|
||||
|
||||
var resource = new DefaultResourceLoader(classloader)
|
||||
.getResource(handledResourceName);
|
||||
return new SpringResourceTemplateResource(resource, characterEncoding);
|
||||
}
|
||||
|
||||
MatchResult matchPluginTemplate(String ownerTemplate, String template) {
|
||||
boolean matches = false;
|
||||
String pluginName = null;
|
||||
String templateName = template;
|
||||
String ownerTemplateName = ownerTemplate;
|
||||
if (StringUtils.isNotBlank(ownerTemplate)) {
|
||||
Matcher ownerTemplateMatcher = PLUGIN_TEMPLATE_PATTERN.matcher(ownerTemplate);
|
||||
if (ownerTemplateMatcher.matches()) {
|
||||
matches = true;
|
||||
pluginName = ownerTemplateMatcher.group(1);
|
||||
ownerTemplateName = ownerTemplateMatcher.group(2);
|
||||
}
|
||||
}
|
||||
Matcher templateMatcher = PLUGIN_TEMPLATE_PATTERN.matcher(template);
|
||||
if (templateMatcher.matches()) {
|
||||
matches = true;
|
||||
pluginName = templateMatcher.group(1);
|
||||
templateName = templateMatcher.group(2);
|
||||
}
|
||||
return new MatchResult(pluginName, ownerTemplateName, templateName, matches);
|
||||
}
|
||||
|
||||
record MatchResult(String pluginName, String ownerTemplateName, String templateName,
|
||||
boolean matches) {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ClassLoader getClassloaderByPlugin(String pluginName) {
|
||||
if (SYSTEM_PLUGIN_NAME.equals(pluginName)) {
|
||||
return this.getClass().getClassLoader();
|
||||
}
|
||||
var pluginWrapper = haloPluginManager.getPlugin(pluginName);
|
||||
if (pluginWrapper == null || !PluginState.STARTED.equals(pluginWrapper.getPluginState())) {
|
||||
return null;
|
||||
}
|
||||
return pluginWrapper.getPluginClassLoader();
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import run.halo.app.extension.ReactiveExtensionClient;
|
|||
import run.halo.app.infra.AnonymousUserConst;
|
||||
import run.halo.app.infra.exception.NotFoundException;
|
||||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
import run.halo.app.theme.ViewNameResolver;
|
||||
import run.halo.app.theme.finders.PostPublicQueryService;
|
||||
import run.halo.app.theme.finders.SinglePageConversionService;
|
||||
import run.halo.app.theme.finders.vo.ContributorVo;
|
||||
|
|
|
@ -35,6 +35,7 @@ import run.halo.app.extension.controller.ControllerBuilder;
|
|||
import run.halo.app.extension.controller.Reconciler;
|
||||
import run.halo.app.infra.exception.NotFoundException;
|
||||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
import run.halo.app.theme.ViewNameResolver;
|
||||
import run.halo.app.theme.finders.SinglePageFinder;
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,7 @@ import run.halo.app.infra.SystemSetting;
|
|||
import run.halo.app.infra.exception.NotFoundException;
|
||||
import run.halo.app.infra.utils.PathUtils;
|
||||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
import run.halo.app.theme.ViewNameResolver;
|
||||
import run.halo.app.theme.finders.PostFinder;
|
||||
import run.halo.app.theme.finders.vo.CategoryVo;
|
||||
import run.halo.app.theme.finders.vo.ListedPostVo;
|
||||
|
@ -30,7 +31,6 @@ import run.halo.app.theme.router.ModelConst;
|
|||
import run.halo.app.theme.router.PageUrlUtils;
|
||||
import run.halo.app.theme.router.TitleVisibilityIdentifyCalculator;
|
||||
import run.halo.app.theme.router.UrlContextListResult;
|
||||
import run.halo.app.theme.router.ViewNameResolver;
|
||||
|
||||
/**
|
||||
* The {@link CategoryPostRouteFactory} for generate {@link RouterFunction} specific to the template
|
||||
|
|
|
@ -35,12 +35,12 @@ import run.halo.app.extension.ReactiveExtensionClient;
|
|||
import run.halo.app.infra.exception.NotFoundException;
|
||||
import run.halo.app.infra.utils.JsonUtils;
|
||||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
import run.halo.app.theme.ViewNameResolver;
|
||||
import run.halo.app.theme.finders.PostFinder;
|
||||
import run.halo.app.theme.finders.vo.PostVo;
|
||||
import run.halo.app.theme.router.ModelMapUtils;
|
||||
import run.halo.app.theme.router.ReactiveQueryPostPredicateResolver;
|
||||
import run.halo.app.theme.router.TitleVisibilityIdentifyCalculator;
|
||||
import run.halo.app.theme.router.ViewNameResolver;
|
||||
|
||||
/**
|
||||
* The {@link PostRouteFactory} for generate {@link RouterFunction} specific to the template
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package run.halo.app.theme.router;
|
||||
package run.halo.app.theme;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
@ -24,11 +24,9 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
|
|||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import run.halo.app.theme.ThemeContext;
|
||||
import run.halo.app.theme.ThemeResolver;
|
||||
|
||||
/**
|
||||
* Tests for {@link ViewNameResolver}.
|
||||
* Tests for {@link DefaultViewNameResolver}.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.0
|
||||
|
@ -43,7 +41,7 @@ class ViewNameResolverTest {
|
|||
private ThymeleafProperties thymeleafProperties;
|
||||
|
||||
@InjectMocks
|
||||
private ViewNameResolver viewNameResolver;
|
||||
private DefaultViewNameResolver viewNameResolver;
|
||||
|
||||
@TempDir
|
||||
private File themePath;
|
|
@ -0,0 +1,53 @@
|
|||
package run.halo.app.theme.engine;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import run.halo.app.plugin.HaloPluginManager;
|
||||
|
||||
/**
|
||||
* Tests for {@link PluginClassloaderTemplateResolver}.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.11.0
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PluginClassloaderTemplateResolverTest {
|
||||
|
||||
@Mock
|
||||
private HaloPluginManager haloPluginManager;
|
||||
|
||||
@InjectMocks
|
||||
private PluginClassloaderTemplateResolver templateResolver;
|
||||
|
||||
@Test
|
||||
void matchPluginTemplateWhenOwnerTemplateMatch() {
|
||||
var result =
|
||||
templateResolver.matchPluginTemplate("plugin:fake-plugin:doc", "modules/layout");
|
||||
assertThat(result.matches()).isTrue();
|
||||
assertThat(result.pluginName()).isEqualTo("fake-plugin");
|
||||
assertThat(result.templateName()).isEqualTo("modules/layout");
|
||||
assertThat(result.ownerTemplateName()).isEqualTo("doc");
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchPluginTemplateWhenDoesNotMatch() {
|
||||
var result =
|
||||
templateResolver.matchPluginTemplate("doc", "modules/layout");
|
||||
assertThat(result.matches()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchPluginTemplateWhenTemplateMatch() {
|
||||
var result =
|
||||
templateResolver.matchPluginTemplate("doc", "plugin:fake-plugin:modules/layout");
|
||||
assertThat(result.matches()).isTrue();
|
||||
assertThat(result.pluginName()).isEqualTo("fake-plugin");
|
||||
assertThat(result.templateName()).isEqualTo("modules/layout");
|
||||
assertThat(result.ownerTemplateName()).isEqualTo("doc");
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ import org.springframework.security.test.context.support.WithMockUser;
|
|||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.function.server.HandlerStrategies;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
@ -26,6 +27,7 @@ import run.halo.app.core.extension.content.SinglePage;
|
|||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.infra.AnonymousUserConst;
|
||||
import run.halo.app.theme.ViewNameResolver;
|
||||
import run.halo.app.theme.finders.PostPublicQueryService;
|
||||
import run.halo.app.theme.finders.SinglePageConversionService;
|
||||
import run.halo.app.theme.finders.vo.ContributorVo;
|
||||
|
@ -99,8 +101,8 @@ class PreviewRouterFunctionTest {
|
|||
when(postPublicQueryService.convertToVo(eq(post), eq(post.getSpec().getHeadSnapshot())))
|
||||
.thenReturn(Mono.just(postVo));
|
||||
|
||||
when(viewNameResolver.resolveViewNameOrDefault(any(), eq("postTemplate"),
|
||||
eq("post"))).thenReturn(Mono.just("postView"));
|
||||
when(viewNameResolver.resolveViewNameOrDefault(any(ServerRequest.class),
|
||||
eq("postTemplate"), eq("post"))).thenReturn(Mono.just("postView"));
|
||||
|
||||
webTestClient.get().uri("/preview/posts/post1")
|
||||
.exchange()
|
||||
|
@ -135,8 +137,8 @@ class PreviewRouterFunctionTest {
|
|||
when(singlePageConversionService.convertToVo(singlePage, "snapshot1"))
|
||||
.thenReturn(Mono.just(singlePageVo));
|
||||
|
||||
when(viewNameResolver.resolveViewNameOrDefault(any(), eq("pageTemplate"),
|
||||
eq("page"))).thenReturn(Mono.just("pageView"));
|
||||
when(viewNameResolver.resolveViewNameOrDefault(any(ServerRequest.class),
|
||||
eq("pageTemplate"), eq("page"))).thenReturn(Mono.just("pageView"));
|
||||
|
||||
webTestClient.get().uri("/preview/singlepages/page1")
|
||||
.exchange()
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.springframework.web.reactive.function.server.HandlerFunction;
|
|||
import org.springframework.web.reactive.function.server.HandlerStrategies;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
@ -47,6 +48,7 @@ import run.halo.app.extension.GroupVersionKind;
|
|||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.extension.controller.Reconciler;
|
||||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
import run.halo.app.theme.ViewNameResolver;
|
||||
import run.halo.app.theme.finders.SinglePageFinder;
|
||||
import run.halo.app.theme.finders.vo.SinglePageVo;
|
||||
import run.halo.app.theme.router.SinglePageRoute.NameSlugPair;
|
||||
|
@ -84,7 +86,7 @@ class SinglePageRouteTest {
|
|||
@Test
|
||||
void handlerFunction() {
|
||||
// fix gh-3448
|
||||
when(viewNameResolver.resolveViewNameOrDefault(any(), any(), any()))
|
||||
when(viewNameResolver.resolveViewNameOrDefault(any(ServerRequest.class), any(), any()))
|
||||
.thenReturn(Mono.just(DefaultTemplateEnum.POST.getValue()));
|
||||
|
||||
String pageName = "fake-page";
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.springframework.context.i18n.SimpleLocaleContext;
|
|||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.i18n.LocaleContextResolver;
|
||||
|
@ -26,6 +27,7 @@ import run.halo.app.extension.GroupVersionKind;
|
|||
import run.halo.app.extension.MetadataUtil;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
import run.halo.app.theme.ViewNameResolver;
|
||||
import run.halo.app.theme.finders.PostFinder;
|
||||
import run.halo.app.theme.finders.vo.PostVo;
|
||||
import run.halo.app.theme.router.DefaultQueryPostPredicateResolver;
|
||||
|
@ -33,7 +35,6 @@ import run.halo.app.theme.router.EmptyView;
|
|||
import run.halo.app.theme.router.ModelConst;
|
||||
import run.halo.app.theme.router.ReactiveQueryPostPredicateResolver;
|
||||
import run.halo.app.theme.router.TitleVisibilityIdentifyCalculator;
|
||||
import run.halo.app.theme.router.ViewNameResolver;
|
||||
|
||||
/**
|
||||
* Tests for {@link PostRouteFactory}.
|
||||
|
@ -77,7 +78,7 @@ class PostRouteFactoryTest extends RouteFactoryTestSuite {
|
|||
|
||||
when(client.fetch(eq(Post.class), eq("fake-name"))).thenReturn(Mono.just(post));
|
||||
|
||||
when(viewNameResolver.resolveViewNameOrDefault(any(), any(), any()))
|
||||
when(viewNameResolver.resolveViewNameOrDefault(any(ServerRequest.class), any(), any()))
|
||||
.thenReturn(Mono.just(DefaultTemplateEnum.POST.getValue()));
|
||||
when(predicateResolver.getPredicate())
|
||||
.thenReturn(new DefaultQueryPostPredicateResolver().getPredicate());
|
||||
|
|
Loading…
Reference in New Issue