refactor: fill in the name of the rendered template as the template id to the view model (#2626)

#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0

#### What this PR does / why we need it:
渲染模板页面时将被渲染的模板名称填充到试图模型中作为 templateId
为什么需要它:
1. thymeleaf 渲染模板可以使用 fragment,此时在 thymeleaf 的 IElementTagProcessor 等处理器中获取到的 template name不一定是例如 post.html这样的名称
2. 比如渲染文章模板 post.html 而 #2569 计划将要在 theme.yaml 中通过配置允许用户选择文章所渲染的模板,因为无法确定渲染模板的标志,例如有些处理器想在文章页插入 head 这样的需求就需要知道哪个模板是文章页。see also [issuecomment-1215135195](https://github.com/halo-dev/halo/issues/2322#issuecomment-1215135195)
所以目前想到的是通过在 render 时填充一个 templateId 到 view model 中标识模板身份
#### Which issue(s) this PR fixes:

Fixes #2621

#### Special notes for your reviewer:
how to test it?
见 issue #2621

/cc @halo-dev/sig-halo 
#### Does this PR introduce a user-facing change?

```release-note
修复设置了文章的 htmlMetas 字段但没有在页面的 head 注入标签的问题
```
pull/2629/head
guqing 2022-10-26 11:12:10 +08:00 committed by GitHub
parent 7c3fc3ac02
commit 3ec7e31cac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 50 additions and 33 deletions

View File

@ -11,6 +11,7 @@ import org.thymeleaf.processor.element.IElementModelStructureHandler;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.PostFinder; import run.halo.app.theme.finders.PostFinder;
import run.halo.app.theme.router.strategy.ModelConst;
/** /**
* <p>The <code>head</code> html snippet injection processor for post template.</p> * <p>The <code>head</code> html snippet injection processor for post template.</p>
@ -31,9 +32,10 @@ public class PostTemplateHeadProcessor implements TemplateHeadProcessor {
@Override @Override
public Mono<Void> process(ITemplateContext context, IModel model, public Mono<Void> process(ITemplateContext context, IModel model,
IElementModelStructureHandler structureHandler) { IElementModelStructureHandler structureHandler) {
return Mono.just(context.getTemplateData().getTemplate()) if (!isPostTemplate(context)) {
.filter(this::isPostTemplate) return Mono.empty();
.map(template -> (String) context.getVariable(POST_NAME_VARIABLE)) }
return Mono.justOrEmpty((String) context.getVariable(POST_NAME_VARIABLE))
.map(postFinder::getByName) .map(postFinder::getByName)
.doOnNext(postVo -> { .doOnNext(postVo -> {
List<Map<String, String>> htmlMetas = postVo.getSpec().getHtmlMetas(); List<Map<String, String>> htmlMetas = postVo.getSpec().getHtmlMetas();
@ -59,7 +61,8 @@ public class PostTemplateHeadProcessor implements TemplateHeadProcessor {
return sb.toString(); return sb.toString();
} }
private boolean isPostTemplate(String template) { private boolean isPostTemplate(ITemplateContext context) {
return DefaultTemplateEnum.POST.getValue().equals(template); return DefaultTemplateEnum.POST.getValue()
.equals(context.getVariable(ModelConst.TEMPLATE_ID));
} }
} }

View File

@ -10,6 +10,7 @@ import reactor.core.publisher.Mono;
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher; import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
import run.halo.app.infra.SystemSetting; import run.halo.app.infra.SystemSetting;
import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.router.strategy.ModelConst;
/** /**
* <p>Global custom head snippet injection for theme global setting.</p> * <p>Global custom head snippet injection for theme global setting.</p>
@ -39,8 +40,7 @@ public class TemplateGlobalHeadProcessor implements TemplateHeadProcessor {
// add content head to model // add content head to model
String contentHeader = codeInjection.getContentHead(); String contentHeader = codeInjection.getContentHead();
String template = context.getTemplateData().getTemplate(); if (StringUtils.isNotBlank(contentHeader) && isContentTemplate(context)) {
if (StringUtils.isNotBlank(contentHeader) && isContentTemplate(template)) {
model.add(modelFactory.createText(contentHeader + "\n")); model.add(modelFactory.createText(contentHeader + "\n"));
} }
}) })
@ -51,8 +51,9 @@ public class TemplateGlobalHeadProcessor implements TemplateHeadProcessor {
return fetcher.fetch(SystemSetting.CodeInjection.GROUP, SystemSetting.CodeInjection.class); return fetcher.fetch(SystemSetting.CodeInjection.GROUP, SystemSetting.CodeInjection.class);
} }
private boolean isContentTemplate(String template) { private boolean isContentTemplate(ITemplateContext context) {
// TODO includes custom page template String templateId = (String) context.getVariable(ModelConst.TEMPLATE_ID);
return DefaultTemplateEnum.POST.getValue().equals(template); return DefaultTemplateEnum.POST.getValue().equals(templateId)
|| DefaultTemplateEnum.SINGLE_PAGE.getValue().equals(templateId);
} }
} }

View File

@ -36,7 +36,8 @@ public class CategoriesRouteStrategy implements ListPageRouteHandlerStrategy {
public HandlerFunction<ServerResponse> getHandler() { public HandlerFunction<ServerResponse> getHandler() {
return request -> ServerResponse.ok() return request -> ServerResponse.ok()
.render(DefaultTemplateEnum.CATEGORIES.getValue(), .render(DefaultTemplateEnum.CATEGORIES.getValue(),
Map.of("categories", categories())); Map.of("categories", categories(),
ModelConst.TEMPLATE_ID, DefaultTemplateEnum.CATEGORIES.getValue()));
} }
@Override @Override

View File

@ -65,7 +65,8 @@ public class CategoryRouteStrategy implements DetailsPageRouteHandlerStrategy {
.render(DefaultTemplateEnum.CATEGORY.getValue(), .render(DefaultTemplateEnum.CATEGORY.getValue(),
Map.of("name", name, Map.of("name", name,
"posts", postListByCategoryName(name, request), "posts", postListByCategoryName(name, request),
"category", categoryByName(name))); "category", categoryByName(name),
ModelConst.TEMPLATE_ID, DefaultTemplateEnum.CATEGORY.getValue()));
} }
@Override @Override

View File

@ -48,7 +48,8 @@ public class IndexRouteStrategy implements ListPageRouteHandlerStrategy {
public HandlerFunction<ServerResponse> getHandler() { public HandlerFunction<ServerResponse> getHandler() {
return request -> ServerResponse.ok() return request -> ServerResponse.ok()
.render(DefaultTemplateEnum.INDEX.getValue(), .render(DefaultTemplateEnum.INDEX.getValue(),
Map.of("posts", postList(request))); Map.of("posts", postList(request),
ModelConst.TEMPLATE_ID, DefaultTemplateEnum.INDEX.getValue()));
} }
@Override @Override

View File

@ -0,0 +1,12 @@
package run.halo.app.theme.router.strategy;
/**
* Static variable keys for view model.
*
* @author guqing
* @since 2.0.0
*/
public enum ModelConst {
;
public static final String TEMPLATE_ID = "_templateId";
}

View File

@ -1,17 +0,0 @@
package run.halo.app.theme.router.strategy;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.springframework.web.reactive.function.server.RequestPredicates.method;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import org.springframework.http.HttpMethod;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.util.UriUtils;
public enum PermalinkPredicates {
;
public static RequestPredicate get(String permalink) {
return method(HttpMethod.GET).and(path(UriUtils.decode(permalink, UTF_8)));
}
}

View File

@ -50,8 +50,12 @@ public class PostRouteStrategy implements DetailsPageRouteHandlerStrategy {
model.putAll(pathMatchInfo.getUriVariables()); model.putAll(pathMatchInfo.getUriVariables());
} }
model.put("post", postByName(name)); model.put("post", postByName(name));
// used by HaloTrackerProcessor
model.put("groupVersionKind", groupVersionKind); model.put("groupVersionKind", groupVersionKind);
model.put("plural", gvk.plural()); model.put("plural", gvk.plural());
// used by TemplateGlobalHeadProcessor and PostTemplateHeadProcessor
model.put(ModelConst.TEMPLATE_ID, DefaultTemplateEnum.POST.getValue());
return ServerResponse.ok() return ServerResponse.ok()
.render(DefaultTemplateEnum.POST.getValue(), model); .render(DefaultTemplateEnum.POST.getValue(), model);
}; };

View File

@ -48,7 +48,10 @@ public class SinglePageRouteStrategy implements DetailsPageRouteHandlerStrategy
Map.of("name", name, Map.of("name", name,
"groupVersionKind", gvk, "groupVersionKind", gvk,
"plural", getPlural(), "plural", getPlural(),
"singlePage", singlePageByName(name))); "singlePage", singlePageByName(name),
ModelConst.TEMPLATE_ID, DefaultTemplateEnum.SINGLE_PAGE.getValue()
)
);
} }
@Override @Override

View File

@ -65,7 +65,9 @@ public class TagRouteStrategy implements DetailsPageRouteHandlerStrategy {
.render(DefaultTemplateEnum.TAG.getValue(), .render(DefaultTemplateEnum.TAG.getValue(),
Map.of("name", name, Map.of("name", name,
"posts", postList(request, name), "posts", postList(request, name),
"tag", tagByName(name)) "tag", tagByName(name),
ModelConst.TEMPLATE_ID, DefaultTemplateEnum.TAG.getValue()
)
); );
} }

View File

@ -37,7 +37,10 @@ public class TagsRouteStrategy implements ListPageRouteHandlerStrategy {
public HandlerFunction<ServerResponse> getHandler() { public HandlerFunction<ServerResponse> getHandler() {
return request -> ServerResponse.ok() return request -> ServerResponse.ok()
.render(DefaultTemplateEnum.TAGS.getValue(), .render(DefaultTemplateEnum.TAGS.getValue(),
Map.of("tags", tags())); Map.of("tags", tags(),
ModelConst.TEMPLATE_ID, DefaultTemplateEnum.TAGS.getValue()
)
);
} }
@Override @Override

View File

@ -34,6 +34,7 @@ import run.halo.app.plugin.ExtensionComponentsFinder;
import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.PostFinder; import run.halo.app.theme.finders.PostFinder;
import run.halo.app.theme.finders.vo.PostVo; import run.halo.app.theme.finders.vo.PostVo;
import run.halo.app.theme.router.strategy.ModelConst;
/** /**
* Tests for {@link HaloProcessorDialect}. * Tests for {@link HaloProcessorDialect}.
@ -128,6 +129,8 @@ class HaloProcessorDialectTest {
void contentHeadAndFooterAndPostProcessors() { void contentHeadAndFooterAndPostProcessors() {
Context context = getContext(); Context context = getContext();
context.setVariable("name", "fake-post"); context.setVariable("name", "fake-post");
// template id flag is used by TemplateGlobalHeadProcessor
context.setVariable(ModelConst.TEMPLATE_ID, DefaultTemplateEnum.POST.getValue());
List<Map<String, String>> htmlMetas = new ArrayList<>(); List<Map<String, String>> htmlMetas = new ArrayList<>();
htmlMetas.add(ImmutableSortedMap.of("name", "post-meta-V1", "content", "post-meta-V1")); htmlMetas.add(ImmutableSortedMap.of("name", "post-meta-V1", "content", "post-meta-V1"));