diff --git a/src/main/java/run/halo/app/theme/dialect/DefaultFaviconHeadProcessor.java b/src/main/java/run/halo/app/theme/dialect/DefaultFaviconHeadProcessor.java
new file mode 100644
index 000000000..d8986ea64
--- /dev/null
+++ b/src/main/java/run/halo/app/theme/dialect/DefaultFaviconHeadProcessor.java
@@ -0,0 +1,46 @@
+package run.halo.app.theme.dialect;
+
+import lombok.AllArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+import org.thymeleaf.context.ITemplateContext;
+import org.thymeleaf.model.IModel;
+import org.thymeleaf.model.IModelFactory;
+import org.thymeleaf.processor.element.IElementModelStructureHandler;
+import reactor.core.publisher.Mono;
+import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
+import run.halo.app.infra.SystemSetting;
+
+/**
+ * Theme template head
tag snippet injection processor for favicon.
+ *
+ * @author guqing
+ * @since 2.0.0
+ */
+@Component
+@AllArgsConstructor
+public class DefaultFaviconHeadProcessor implements TemplateHeadProcessor {
+
+ private final SystemConfigurableEnvironmentFetcher fetcher;
+
+ @Override
+ public Mono process(ITemplateContext context, IModel model,
+ IElementModelStructureHandler structureHandler) {
+ return fetchBasicSetting()
+ .filter(basic -> StringUtils.isNotBlank(basic.getFavicon()))
+ .map(basic -> {
+ IModelFactory modelFactory = context.getModelFactory();
+ model.add(modelFactory.createText(faviconSnippet(basic.getFavicon())));
+ return model;
+ })
+ .then();
+ }
+
+ private String faviconSnippet(String favicon) {
+ return String.format("\n", favicon);
+ }
+
+ private Mono fetchBasicSetting() {
+ return fetcher.fetch(SystemSetting.Basic.GROUP, SystemSetting.Basic.class);
+ }
+}
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 ee5960739..74c219dfc 100644
--- a/src/test/java/run/halo/app/theme/dialect/HaloProcessorDialectTest.java
+++ b/src/test/java/run/halo/app/theme/dialect/HaloProcessorDialectTest.java
@@ -69,6 +69,7 @@ class HaloProcessorDialectTest {
Map map = new HashMap<>();
map.put("postTemplateHeadProcessor", new PostTemplateHeadProcessor(postFinder));
map.put("templateGlobalHeadProcessor", new TemplateGlobalHeadProcessor(fetcher));
+ map.put("faviconHeadProcessor", new DefaultFaviconHeadProcessor(fetcher));
lenient().when(applicationContext.getBeansOfType(TemplateHeadProcessor.class))
.thenReturn(map);
@@ -85,6 +86,11 @@ class HaloProcessorDialectTest {
@Test
void globalHeadAndFooterProcessors() {
+ SystemSetting.Basic basic = new SystemSetting.Basic();
+ basic.setFavicon("favicon.ico");
+ when(fetcher.fetch(eq(SystemSetting.Basic.GROUP),
+ eq(SystemSetting.Basic.class))).thenReturn(Mono.just(basic));
+
Context context = getContext();
String result = templateEngine.process("index", context);
@@ -95,6 +101,7 @@ class HaloProcessorDialectTest {
Index
+
index
@@ -124,6 +131,11 @@ class HaloProcessorDialectTest {
.metadata(metadata).build();
when(postFinder.getByName(eq("fake-post"))).thenReturn(postVo);
+ SystemSetting.Basic basic = new SystemSetting.Basic();
+ basic.setFavicon(null);
+ when(fetcher.fetch(eq(SystemSetting.Basic.GROUP),
+ eq(SystemSetting.Basic.class))).thenReturn(Mono.just(basic));
+
String result = templateEngine.process("post", context);
assertThat(result).isEqualTo("""