mirror of https://github.com/halo-dev/halo
feat: head tag supports extension for template (#2574)
#### What type of PR is this? /kind feature /milestone 2.0 /area core #### What this PR does / why we need it: 允许插件通过实现 TemplateHeadProcessor 接口来修改主题模板的 head 标签 #### Which issue(s) this PR fixes: how to test it? 1. 克隆 https://github.com/halo-sigs/plugin-umami 2. build 一个 jar 包作为插件使用 3. 配置 plugin-umami 后能在主题页的 head 标签看到一个用于 umami 统计的 script 标签 Fixes # #### Special notes for your reviewer: /cc @halo-dev/sig-halo #### Does this PR introduce a user-facing change? ```release-note None ```pull/2537/head
parent
3973768a7a
commit
3d79484591
|
@ -9,6 +9,7 @@ import org.thymeleaf.processor.element.AbstractElementModelProcessor;
|
|||
import org.thymeleaf.processor.element.IElementModelStructureHandler;
|
||||
import org.thymeleaf.spring6.context.SpringContextUtils;
|
||||
import org.thymeleaf.templatemode.TemplateMode;
|
||||
import run.halo.app.plugin.ExtensionComponentsFinder;
|
||||
|
||||
/**
|
||||
* Global head injection processor.
|
||||
|
@ -50,10 +51,6 @@ public class GlobalHeadInjectionProcessor extends AbstractElementModelProcessor
|
|||
structureHandler.setLocalVariable(PROCESS_FLAG, true);
|
||||
|
||||
// handle <head> tag
|
||||
/*
|
||||
* Obtain the Spring application context.
|
||||
*/
|
||||
final ApplicationContext appCtx = SpringContextUtils.getApplicationContext(context);
|
||||
|
||||
/*
|
||||
* Create the DOM structure that will be substituting our custom tag.
|
||||
|
@ -65,7 +62,8 @@ public class GlobalHeadInjectionProcessor extends AbstractElementModelProcessor
|
|||
|
||||
// apply processors to modelToInsert
|
||||
Collection<TemplateHeadProcessor> templateHeadProcessors =
|
||||
getTemplateHeadProcessors(appCtx);
|
||||
getTemplateHeadProcessors(context);
|
||||
|
||||
for (TemplateHeadProcessor processor : templateHeadProcessors) {
|
||||
processor.process(context, modelToInsert, structureHandler)
|
||||
.block();
|
||||
|
@ -75,8 +73,10 @@ public class GlobalHeadInjectionProcessor extends AbstractElementModelProcessor
|
|||
model.insertModel(model.size() - 1, modelToInsert);
|
||||
}
|
||||
|
||||
private Collection<TemplateHeadProcessor> getTemplateHeadProcessors(ApplicationContext ctx) {
|
||||
return ctx.getBeansOfType(TemplateHeadProcessor.class)
|
||||
.values();
|
||||
private Collection<TemplateHeadProcessor> getTemplateHeadProcessors(ITemplateContext context) {
|
||||
ApplicationContext appCtx = SpringContextUtils.getApplicationContext(context);
|
||||
ExtensionComponentsFinder componentsFinder =
|
||||
appCtx.getBean(ExtensionComponentsFinder.class);
|
||||
return componentsFinder.getExtensions(TemplateHeadProcessor.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package run.halo.app.theme.dialect;
|
||||
|
||||
import org.pf4j.ExtensionPoint;
|
||||
import org.thymeleaf.context.ITemplateContext;
|
||||
import org.thymeleaf.model.IModel;
|
||||
import org.thymeleaf.processor.element.IElementModelStructureHandler;
|
||||
|
@ -12,7 +13,7 @@ import reactor.core.publisher.Mono;
|
|||
* @since 2.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface TemplateHeadProcessor {
|
||||
public interface TemplateHeadProcessor extends ExtensionPoint {
|
||||
|
||||
Mono<Void> process(ITemplateContext context, IModel model,
|
||||
IElementModelStructureHandler structureHandler);
|
||||
|
|
|
@ -30,6 +30,7 @@ import run.halo.app.core.extension.Post;
|
|||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
|
||||
import run.halo.app.infra.SystemSetting;
|
||||
import run.halo.app.plugin.ExtensionComponentsFinder;
|
||||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
import run.halo.app.theme.finders.PostFinder;
|
||||
import run.halo.app.theme.finders.vo.PostVo;
|
||||
|
@ -57,6 +58,9 @@ class HaloProcessorDialectTest {
|
|||
@Mock
|
||||
private SystemConfigurableEnvironmentFetcher fetcher;
|
||||
|
||||
@Mock
|
||||
private ExtensionComponentsFinder extensionComponentsFinder;
|
||||
|
||||
private TemplateEngine templateEngine;
|
||||
|
||||
@BeforeEach
|
||||
|
@ -70,7 +74,7 @@ class HaloProcessorDialectTest {
|
|||
map.put("postTemplateHeadProcessor", new PostTemplateHeadProcessor(postFinder));
|
||||
map.put("templateGlobalHeadProcessor", new TemplateGlobalHeadProcessor(fetcher));
|
||||
map.put("faviconHeadProcessor", new DefaultFaviconHeadProcessor(fetcher));
|
||||
lenient().when(applicationContext.getBeansOfType(TemplateHeadProcessor.class))
|
||||
lenient().when(applicationContext.getBeansOfType(eq(TemplateHeadProcessor.class)))
|
||||
.thenReturn(map);
|
||||
|
||||
SystemSetting.CodeInjection codeInjection = new SystemSetting.CodeInjection();
|
||||
|
@ -80,8 +84,14 @@ class HaloProcessorDialectTest {
|
|||
when(fetcher.fetch(eq(SystemSetting.CodeInjection.GROUP),
|
||||
eq(SystemSetting.CodeInjection.class))).thenReturn(Mono.just(codeInjection));
|
||||
|
||||
when(applicationContext.getBean(SystemConfigurableEnvironmentFetcher.class))
|
||||
when(applicationContext.getBean(eq(SystemConfigurableEnvironmentFetcher.class)))
|
||||
.thenReturn(fetcher);
|
||||
|
||||
when(applicationContext.getBean(eq(ExtensionComponentsFinder.class)))
|
||||
.thenReturn(extensionComponentsFinder);
|
||||
|
||||
when(extensionComponentsFinder.getExtensions(eq(TemplateHeadProcessor.class)))
|
||||
.thenReturn(new ArrayList<>(map.values()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue