diff --git a/application/src/main/java/run/halo/app/plugin/extensionpoint/DefaultExtensionGetter.java b/application/src/main/java/run/halo/app/plugin/extensionpoint/DefaultExtensionGetter.java index 78520d07e..2c7c7e98d 100644 --- a/application/src/main/java/run/halo/app/plugin/extensionpoint/DefaultExtensionGetter.java +++ b/application/src/main/java/run/halo/app/plugin/extensionpoint/DefaultExtensionGetter.java @@ -1,11 +1,14 @@ package run.halo.app.plugin.extensionpoint; import java.util.Comparator; +import java.util.List; import java.util.Set; import java.util.stream.Stream; import lombok.RequiredArgsConstructor; import org.pf4j.ExtensionPoint; import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -32,14 +35,17 @@ public class DefaultExtensionGetter implements ExtensionGetter { .switchIfEmpty(Mono.just(ExtensionPointEnabled.EMPTY)) .mapNotNull(enabled -> { var implClassNames = enabled.getOrDefault(extensionPoint.getName(), Set.of()); - return pluginManager.getExtensions(extensionPoint) + List allExtensions = getAllExtensions(extensionPoint); + if (allExtensions.isEmpty()) { + return null; + } + return allExtensions .stream() .filter(impl -> implClassNames.contains(impl.getClass().getName())) .findFirst() // Fallback to local implementation of the extension point. // This will happen when no proper configuration is found. - .orElseGet(() -> - applicationContext.getBeanProvider(extensionPoint).getIfAvailable()); + .orElseGet(() -> allExtensions.get(0)); }); } @@ -74,16 +80,23 @@ public class DefaultExtensionGetter implements ExtensionGetter { if (type == ExtensionPointDefinition.ExtensionPointType.SINGLETON) { return getEnabledExtension(extensionPoint).flux(); } - Stream pluginExtsStream = pluginManager.getExtensions(extensionPoint) - .stream(); - Stream systemExtsStream = applicationContext.getBeanProvider(extensionPoint) - .orderedStream(); + // TODO If the type is sortable, may need to process the returned order. - return Flux.just(pluginExtsStream, systemExtsStream) - .flatMap(Flux::fromStream); + return Flux.fromIterable(getAllExtensions(extensionPoint)); }); } + @NonNull + List getAllExtensions(Class extensionPoint) { + Stream pluginExtsStream = pluginManager.getExtensions(extensionPoint) + .stream(); + Stream systemExtsStream = applicationContext.getBeanProvider(extensionPoint) + .orderedStream(); + return Stream.concat(systemExtsStream, pluginExtsStream) + .sorted(new AnnotationAwareOrderComparator()) + .toList(); + } + Mono fetchExtensionPointDefinition( Class extensionPoint) { // TODO Optimize query diff --git a/application/src/main/java/run/halo/app/theme/dialect/CommentElementTagProcessor.java b/application/src/main/java/run/halo/app/theme/dialect/CommentElementTagProcessor.java index 7f01467a0..b4db33e1d 100644 --- a/application/src/main/java/run/halo/app/theme/dialect/CommentElementTagProcessor.java +++ b/application/src/main/java/run/halo/app/theme/dialect/CommentElementTagProcessor.java @@ -1,6 +1,5 @@ package run.halo.app.theme.dialect; -import java.util.List; import org.springframework.context.ApplicationContext; import org.thymeleaf.context.ITemplateContext; import org.thymeleaf.model.IProcessableElementTag; @@ -8,7 +7,7 @@ import org.thymeleaf.processor.element.AbstractElementTagProcessor; import org.thymeleaf.processor.element.IElementTagStructureHandler; import org.thymeleaf.spring6.context.SpringContextUtils; import org.thymeleaf.templatemode.TemplateMode; -import run.halo.app.plugin.ExtensionComponentsFinder; +import run.halo.app.plugin.extensionpoint.ExtensionGetter; /** @@ -44,15 +43,15 @@ public class CommentElementTagProcessor extends AbstractElementTagProcessor { protected void doProcess(ITemplateContext context, IProcessableElementTag tag, IElementTagStructureHandler structureHandler) { final ApplicationContext appCtx = SpringContextUtils.getApplicationContext(context); - ExtensionComponentsFinder componentsFinder = - appCtx.getBean(ExtensionComponentsFinder.class); - List commentWidgets = componentsFinder.getExtensions(CommentWidget.class); - if (commentWidgets.isEmpty()) { + ExtensionGetter extensionGetter = appCtx.getBean(ExtensionGetter.class); + CommentWidget commentWidget = + extensionGetter.getEnabledExtensionByDefinition(CommentWidget.class) + .next() + .block(); + if (commentWidget == null) { structureHandler.replaceWith("", false); return; } - // TODO if find more than one comment widget, query CommentWidget setting to decide which - // one to use. - commentWidgets.get(0).render(context, tag, structureHandler); + commentWidget.render(context, tag, structureHandler); } } diff --git a/application/src/main/resources/extensions/extensionpoint-definitions.yaml b/application/src/main/resources/extensions/extensionpoint-definitions.yaml index 3072f6a2a..375b24504 100644 --- a/application/src/main/resources/extensions/extensionpoint-definitions.yaml +++ b/application/src/main/resources/extensions/extensionpoint-definitions.yaml @@ -18,7 +18,7 @@ spec: className: run.halo.app.theme.ReactivePostContentHandler displayName: ReactivePostContentHandler type: MULTI_INSTANCE - description: "Provides a way to extend the post content to be displayed in the theme-side." + description: "Provides a way to extend the post content to be displayed on the theme-side." --- apiVersion: plugin.halo.run/v1alpha1 @@ -29,4 +29,15 @@ spec: className: run.halo.app.theme.ReactiveSinglePageContentHandler displayName: ReactiveSinglePageContentHandler type: MULTI_INSTANCE - description: "Provides a way to extend the single page content to be displayed in the theme-side." + description: "Provides a way to extend the single page content to be displayed on the theme-side." + +--- +apiVersion: plugin.halo.run/v1alpha1 +kind: ExtensionPointDefinition +metadata: + name: comment-widget +spec: + className: run.halo.app.theme.dialect.CommentWidget + displayName: CommentWidget + type: SINGLETON + description: "Provides an extension point for the comment widget on the theme-side." diff --git a/application/src/test/java/run/halo/app/theme/dialect/CommentElementTagProcessorTest.java b/application/src/test/java/run/halo/app/theme/dialect/CommentElementTagProcessorTest.java index c4cc602db..c93bdf373 100644 --- a/application/src/test/java/run/halo/app/theme/dialect/CommentElementTagProcessorTest.java +++ b/application/src/test/java/run/halo/app/theme/dialect/CommentElementTagProcessorTest.java @@ -4,7 +4,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.BeforeEach; @@ -24,7 +23,9 @@ import org.thymeleaf.spring6.expression.ThymeleafEvaluationContext; import org.thymeleaf.templateresolver.StringTemplateResolver; import org.thymeleaf.templateresource.ITemplateResource; import org.thymeleaf.templateresource.StringTemplateResource; +import reactor.core.publisher.Flux; import run.halo.app.plugin.ExtensionComponentsFinder; +import run.halo.app.plugin.extensionpoint.ExtensionGetter; /** * Tests for {@link CommentElementTagProcessor}. @@ -41,7 +42,7 @@ class CommentElementTagProcessorTest { private ApplicationContext applicationContext; @Mock - private ExtensionComponentsFinder componentsFinder; + private ExtensionGetter extensionGetter; private TemplateEngine templateEngine; @@ -51,14 +52,16 @@ class CommentElementTagProcessorTest { templateEngine = new TemplateEngine(); templateEngine.setDialects(Set.of(haloProcessorDialect, new SpringStandardDialect())); templateEngine.addTemplateResolver(new TestTemplateResolver()); - when(applicationContext.getBean(eq(ExtensionComponentsFinder.class))) - .thenReturn(componentsFinder); + when(applicationContext.getBean(eq(ExtensionGetter.class))) + .thenReturn(extensionGetter); } @Test void doProcess() { Context context = getContext(); + when(extensionGetter.getEnabledExtensionByDefinition(eq(CommentWidget.class))) + .thenReturn(Flux.empty()); String result = templateEngine.process("commentWidget", context); assertThat(result).isEqualTo(""" @@ -70,8 +73,8 @@ class CommentElementTagProcessorTest { """); - when(componentsFinder.getExtensions(CommentWidget.class)) - .thenReturn(List.of(new DefaultCommentWidget())); + when(extensionGetter.getEnabledExtensionByDefinition(eq(CommentWidget.class))) + .thenReturn(Flux.just(new DefaultCommentWidget())); result = templateEngine.process("commentWidget", context); assertThat(result).isEqualTo("""