Skip to content

Commit

Permalink
Implement global HTML injection for head and footer (#9)
Browse files Browse the repository at this point in the history
#### What type of PR is this?
/kind feature
#### What this PR does / why we need it:
此 PR 实现了代码全局的HEAD和FOOTER注入,暂时没有实现路径匹配的逻辑
#### Which issue(s) this PR fixes:
Fixes #8 
#### Does this PR introduce a user-facing change?
```release-note
None
```
  • Loading branch information
CaiHaosen authored Aug 13, 2024
1 parent 864f49b commit 4cbd53b
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 3 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ repositories {
}

dependencies {
implementation platform('run.halo.tools.platform:plugin:2.11.0-SNAPSHOT')
implementation platform('run.halo.tools.platform:plugin:2.17.0-SNAPSHOT')
compileOnly 'run.halo.app:api'

testImplementation 'run.halo.app:api'
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/run/halo/injection/AbstractHtmlProcessor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package run.halo.injection;

import java.util.Set;
import org.thymeleaf.context.ITemplateContext;

public abstract class AbstractHtmlProcessor {
protected static final String TEMPLATE_ID_VARIABLE = "_templateId";

protected boolean isContentTemplate(ITemplateContext context) {
return "post".equals(context.getVariable(TEMPLATE_ID_VARIABLE))
|| "page".equals(context.getVariable(TEMPLATE_ID_VARIABLE));
}

// 匹配路径接口
protected boolean isRequestPathMatchingRoute(String requestRoute, Set<String> pageRules) {
return true;
}
}
36 changes: 36 additions & 0 deletions src/main/java/run/halo/injection/HtmlFooterProcessor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package run.halo.injection;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.model.IModel;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.element.IElementTagStructureHandler;
import reactor.core.publisher.Mono;
import run.halo.app.theme.dialect.TemplateFooterProcessor;

@Slf4j
@Component
@RequiredArgsConstructor
public class HtmlFooterProcessor extends AbstractHtmlProcessor implements TemplateFooterProcessor {
private final HtmlService htmlService;

@Override
public Mono<Void> process(ITemplateContext context, IProcessableElementTag tag,
IElementTagStructureHandler structureHandler, IModel model) {
return htmlService.listEnabledInjectionsByPoint(HtmlInjection.InjectionPoint.FOOTER)
.doOnNext(htmlInjection -> {
if (isContentTemplate(context)) {
model.add(
context.getModelFactory().createText(
htmlInjection.getSpec().getFragment()));
}
})
.onErrorResume(e -> {
log.error("HtmlFooterProcessor process failed", e);
return Mono.empty();
})
.then();
}
}
35 changes: 35 additions & 0 deletions src/main/java/run/halo/injection/HtmlHeadProcessor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package run.halo.injection;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.model.IModel;
import org.thymeleaf.processor.element.IElementModelStructureHandler;
import reactor.core.publisher.Mono;
import run.halo.app.theme.dialect.TemplateHeadProcessor;

@Slf4j
@Component
@RequiredArgsConstructor
public class HtmlHeadProcessor extends AbstractHtmlProcessor implements TemplateHeadProcessor {
private final HtmlService htmlService;

@Override
public Mono<Void> process(ITemplateContext context, IModel model,
IElementModelStructureHandler structureHandler) {
return htmlService.listEnabledInjectionsByPoint(HtmlInjection.InjectionPoint.HEADER)
.doOnNext(htmlInjection -> {
if (isContentTemplate(context)) {
model.add(
context.getModelFactory().createText(
htmlInjection.getSpec().getFragment()));
}
})
.onErrorResume(e -> {
log.error("HtmlHeadProcessor process failed", e);
return Mono.empty();
})
.then();
}
}
8 changes: 8 additions & 0 deletions src/main/java/run/halo/injection/HtmlService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package run.halo.injection;

import reactor.core.publisher.Flux;

public interface HtmlService {

Flux<HtmlInjection> listEnabledInjectionsByPoint(HtmlInjection.InjectionPoint injectionPoint);
}
38 changes: 38 additions & 0 deletions src/main/java/run/halo/injection/HtmlServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package run.halo.injection;

import static run.halo.app.extension.index.query.QueryFactory.and;
import static run.halo.app.extension.index.query.QueryFactory.equal;

import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import run.halo.app.extension.ListOptions;
import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.index.query.Query;

@Service
@RequiredArgsConstructor
public class HtmlServiceImpl implements HtmlService {
private final ReactiveExtensionClient client;

/**
* 根据注入点获取正在启用的HtmlInjection 对象.
*/
@Override
public Flux<HtmlInjection> listEnabledInjectionsByPoint(
HtmlInjection.InjectionPoint injectionPoint) {
Query query = and(
equal("spec.enabled", "true"),
equal("spec.injectionPoint", injectionPoint.name())
);

ListOptions options = ListOptions.builder()
.fieldQuery(query)
.build();

return client.listAll(HtmlInjection.class, options,
Sort.by(Sort.Order.asc("metadata.name")));
}
}

23 changes: 21 additions & 2 deletions src/main/java/run/halo/injection/InjectionPlugin.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package run.halo.injection;

import static run.halo.app.extension.index.IndexAttributeFactory.simpleAttribute;

import org.springframework.stereotype.Component;
import run.halo.app.extension.Scheme;
import run.halo.app.extension.SchemeManager;
import run.halo.app.extension.index.IndexSpec;
import run.halo.app.plugin.BasePlugin;
import run.halo.app.plugin.PluginContext;


@Component
public class InjectionPlugin extends BasePlugin {
private final SchemeManager schemeManager;
Expand All @@ -18,7 +20,24 @@ public InjectionPlugin(PluginContext pluginContext, SchemeManager schemeManager)

@Override
public void start() {
schemeManager.register(HtmlInjection.class);
schemeManager.register(HtmlInjection.class, indexSpecs -> {
// 为 enabled 添加索引方便根据启用状态查询
indexSpecs.add(new IndexSpec()
.setName("spec.enabled")
.setIndexFunc(simpleAttribute(HtmlInjection.class, htmlInjection -> {
return Boolean.toString(htmlInjection.getSpec().isEnabled());
}))
);
// 为 injectionPoint 添加索引
indexSpecs.add(new IndexSpec()
.setName("spec.injectionPoint")
.setIndexFunc(simpleAttribute(HtmlInjection.class, htmlInjection -> {
HtmlInjection.InjectionPoint injectionPoint =
htmlInjection.getSpec().getInjectionPoint();
return injectionPoint != null ? injectionPoint.name() : null;
}))
);
});
}

@Override
Expand Down

0 comments on commit 4cbd53b

Please sign in to comment.