mirror of https://github.com/halo-dev/halo
feat: add favicon head processor (#2582)
#### What type of PR is this? /kind feature /area core /milestone 2.0 #### What this PR does / why we need it: 模板渲染自动填充 Favicon 到 head 标签 #### Which issue(s) this PR fixes: Fixes #2581 #### Special notes for your reviewer: how to test it? 在 console 系统设置 -> 基础设置 填写 Favicon 后,到主题端能到看它 /cc @halo-dev/sig-halo #### Does this PR introduce a user-facing change? ```release-note 支持设置 Favicon ```pull/2574/head^2
parent
2527eb42e2
commit
a3448adee2
|
@ -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 <code>head</code> 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<Void> 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("<link rel=\"icon\" href=\"%s\" />\n", favicon);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mono<SystemSetting.Basic> fetchBasicSetting() {
|
||||||
|
return fetcher.fetch(SystemSetting.Basic.GROUP, SystemSetting.Basic.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,6 +69,7 @@ class HaloProcessorDialectTest {
|
||||||
Map<String, TemplateHeadProcessor> map = new HashMap<>();
|
Map<String, TemplateHeadProcessor> map = new HashMap<>();
|
||||||
map.put("postTemplateHeadProcessor", new PostTemplateHeadProcessor(postFinder));
|
map.put("postTemplateHeadProcessor", new PostTemplateHeadProcessor(postFinder));
|
||||||
map.put("templateGlobalHeadProcessor", new TemplateGlobalHeadProcessor(fetcher));
|
map.put("templateGlobalHeadProcessor", new TemplateGlobalHeadProcessor(fetcher));
|
||||||
|
map.put("faviconHeadProcessor", new DefaultFaviconHeadProcessor(fetcher));
|
||||||
lenient().when(applicationContext.getBeansOfType(TemplateHeadProcessor.class))
|
lenient().when(applicationContext.getBeansOfType(TemplateHeadProcessor.class))
|
||||||
.thenReturn(map);
|
.thenReturn(map);
|
||||||
|
|
||||||
|
@ -85,6 +86,11 @@ class HaloProcessorDialectTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void globalHeadAndFooterProcessors() {
|
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();
|
Context context = getContext();
|
||||||
|
|
||||||
String result = templateEngine.process("index", context);
|
String result = templateEngine.process("index", context);
|
||||||
|
@ -95,6 +101,7 @@ class HaloProcessorDialectTest {
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>Index</title>
|
<title>Index</title>
|
||||||
<meta name="global-head-test" content="test" />
|
<meta name="global-head-test" content="test" />
|
||||||
|
<link rel="icon" href="favicon.ico" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p>index</p>
|
<p>index</p>
|
||||||
|
@ -124,6 +131,11 @@ class HaloProcessorDialectTest {
|
||||||
.metadata(metadata).build();
|
.metadata(metadata).build();
|
||||||
when(postFinder.getByName(eq("fake-post"))).thenReturn(postVo);
|
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);
|
String result = templateEngine.process("post", context);
|
||||||
assertThat(result).isEqualTo("""
|
assertThat(result).isEqualTo("""
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
|
Loading…
Reference in New Issue