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