From 3d794845910b3f095ac23c2b270e4e9f63bffa86 Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Tue, 18 Oct 2022 12:18:09 +0800 Subject: [PATCH] feat: head tag supports extension for template (#2574) 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: 允许插件通过实现 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 ``` --- .../dialect/GlobalHeadInjectionProcessor.java | 16 ++++++++-------- .../app/theme/dialect/TemplateHeadProcessor.java | 3 ++- .../theme/dialect/HaloProcessorDialectTest.java | 14 ++++++++++++-- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/run/halo/app/theme/dialect/GlobalHeadInjectionProcessor.java b/src/main/java/run/halo/app/theme/dialect/GlobalHeadInjectionProcessor.java index 2b6e16427..4016ef983 100644 --- a/src/main/java/run/halo/app/theme/dialect/GlobalHeadInjectionProcessor.java +++ b/src/main/java/run/halo/app/theme/dialect/GlobalHeadInjectionProcessor.java @@ -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 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 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 getTemplateHeadProcessors(ApplicationContext ctx) { - return ctx.getBeansOfType(TemplateHeadProcessor.class) - .values(); + private Collection getTemplateHeadProcessors(ITemplateContext context) { + ApplicationContext appCtx = SpringContextUtils.getApplicationContext(context); + ExtensionComponentsFinder componentsFinder = + appCtx.getBean(ExtensionComponentsFinder.class); + return componentsFinder.getExtensions(TemplateHeadProcessor.class); } } diff --git a/src/main/java/run/halo/app/theme/dialect/TemplateHeadProcessor.java b/src/main/java/run/halo/app/theme/dialect/TemplateHeadProcessor.java index 6f8312561..7a167176a 100644 --- a/src/main/java/run/halo/app/theme/dialect/TemplateHeadProcessor.java +++ b/src/main/java/run/halo/app/theme/dialect/TemplateHeadProcessor.java @@ -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 process(ITemplateContext context, IModel model, IElementModelStructureHandler structureHandler); diff --git a/src/test/java/run/halo/app/theme/dialect/HaloProcessorDialectTest.java b/src/test/java/run/halo/app/theme/dialect/HaloProcessorDialectTest.java index 74c219dfc..3b858bedb 100644 --- a/src/test/java/run/halo/app/theme/dialect/HaloProcessorDialectTest.java +++ b/src/test/java/run/halo/app/theme/dialect/HaloProcessorDialectTest.java @@ -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