Skip to content

Latest commit

 

History

History
273 lines (229 loc) · 9.17 KB

File metadata and controls

273 lines (229 loc) · 9.17 KB

Magnolia Templatebuilder

The TemplateBuilder module is a builder for Magnolia templates in a java.

Setup

Add Maven dependency:

<dependency>
    <groupId>com.merkle.oss.magnolia</groupId>
    <artifactId>magnolia-templatebuilder</artifactId>
    <version>1.4.0</version>
</dependency>

DI-Bindings

<module>
    <name>SomeModule</name>
    ...
    <components>
        <id>main</id>
        <configurer>
            <class>GuiceComponentConfigurer</class>
        </configurer>
    </components>
    ...
</module>
import info.magnolia.objectfactory.guice.AbstractGuiceComponentConfigurer;
import info.magnolia.virtualuri.VirtualUriMapping;

import org.apache.commons.lang3.reflect.TypeLiteral;
import org.reflections.Reflections;

import com.google.inject.multibindings.Multibinder;
import com.merkle.oss.magnolia.templatebuilder.annotation.Template;
import com.merkle.oss.magnolia.templatebuilder.annotation.TemplateFactories;

public class GuiceComponentConfigurer extends AbstractGuiceComponentConfigurer {
    @Override
    protected void configure() {
        // Here we use Reflections, but you can also use ClassPathScanningCandidateComponentProvider or bind each factory manually 
        final Multibinder<Class<?>> templateFactoryMultibinder = Multibinder.newSetBinder(binder, new TypeLiteral<>() {}, TemplateFactories.class);
        new Reflections(getClass()).getTypesAnnotatedWith(Template.class).forEach(clazz -> templateFactoryMultibinder.addBinding().toInstance(clazz));
    }
}

How to use

Example

Area-component-category

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.merkle.oss.magnolia.templatebuilder.annotation.area.ComponentCategory;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@ComponentCategory
public @interface ContentArea {}

Component-template

import com.merkle.oss.magnolia.templatebuilder.annotation.Template;

@ContentArea
@Template(
        id = SomeComponent.ID,
        title = "templates.components." + SomeComponent.NAME + ".title",
        dialog = SomeComponentDialog.ID,
        description = "templates.components." + SomeComponent.NAME + ".description",
        renderer = "freemarker",
        templateScript = "/someModule/templates/components/someComponent.ftl"
)
public class SomeComponent extends BaseComponent {
    public static final String NAME = "SomeComponent";
    public static final String ID = "SomeApp:components/" + NAME;
}

Page-template with area

import java.util.List;
import java.util.Set;

import javax.jcr.Node;

import com.merkle.oss.magnolia.templatebuilder.annotation.Available;
import com.merkle.oss.magnolia.templatebuilder.annotation.Template;
import com.merkle.oss.magnolia.templatebuilder.annotation.area.Area;
import com.merkle.oss.magnolia.templatebuilder.annotation.area.AvailableComponentClasses;
import com.merkle.oss.magnolia.templatebuilder.annotation.area.AvailableComponents;

@Template(
        id = SomePage.ID,
        title = "templates.pages." + SomePage.NAME + ".title",
        dialog = SomePageDialog.ID,
        description = "templates.pages." + SomePage.NAME + ".description",
        renderer = "freemarker",
        templateScript = "/someModule/templates/pages/somePage.ftl"
)
public class SomePage {
    public static final String NAME = "SomePage";
    public static final String ID = "SomeApp:pages/" + NAME;

    @Available
    public boolean isAvailable(final Node node) {
        //TODO implement
        return true;
    }

    @Area(
            id = ContentArea.ID,
            name = ContentArea.NAME, 
            title = "templates.areas." + SomePage.ContentArea.NAME + ".title",
            renderer = "freemarker", //optional, uses templates renderer if not specified
            templateScript = "/someModule/templates/areas/contentArea.ftl"
    )
    @AvailableComponentClasses({ ContentArea.class })
    @AvailableComponents({ "someComponentId" })
    public static class ContentArea {
        public static final String NAME = "ContentArea";
        public static final String ID = SomePage.ID + "/" + NAME;
    }
}

Customization

Definition decorators

Implement a decorator:

import info.magnolia.config.registry.DefinitionProvider;
import info.magnolia.config.registry.DefinitionProviderWrapper;
import info.magnolia.config.registry.Registry;
import info.magnolia.config.registry.decoration.DefinitionDecorator;
import info.magnolia.config.registry.decoration.DefinitionDecoratorMetadata;
import info.magnolia.rendering.template.TemplateDefinition;


public class CustomTemplateDefinitionDecorator implements DefinitionDecorator<TemplateDefinition> {
    @Override
    public DefinitionDecoratorMetadata metadata() {
        return () -> "someModule";
    }

    @Override
    public boolean appliesTo(final DefinitionProvider<TemplateDefinition> definitionProvider) {
        //TODO implement
        return true;
    }

    @Override
    public DefinitionProvider<TemplateDefinition> decorate(final DefinitionProvider<TemplateDefinition> definitionProvider) {
        return new DefinitionProviderWrapper<>(definitionProvider) {
            @Override
            public TemplateDefinition get() throws Registry.InvalidDefinitionException {
                //TODO deorate definition
                return definitionProvider.get();
            }
        };
    }
}

Override the TemplateDefinitionProvider.Factory and pass your custom decorator to TemplateDefinitionProvider:

import info.magnolia.objectfactory.Components;
import info.magnolia.rendering.template.configured.ConfiguredAreaDefinition;

import java.util.Collections;
import java.util.List;
import java.util.Set;

import javax.inject.Inject;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import com.merkle.oss.magnolia.templatebuilder.TemplateAvailabilityResolver;
import com.merkle.oss.magnolia.templatebuilder.TemplateDefinitionProvider;

public class CustomTemplateDefinitionProviderFactory extends TemplateDefinitionProvider.Factory {
    private final TemplateAvailabilityResolver templateAvailabilityResolver;
    private final CustomTemplateDefinitionDecorator customTemplateDefinitionDecorator;

    @Inject
    public CustomTemplateDefinitionProviderFactory(
            final TemplateAvailabilityResolver templateAvailabilityResolver,
            final CustomTemplateDefinitionDecorator customTemplateDefinitionDecorator
    ) {
        super(templateAvailabilityResolver);
        this.templateAvailabilityResolver = templateAvailabilityResolver;
        this.customTemplateDefinitionDecorator = customTemplateDefinitionDecorator;
    }

    public TemplateDefinitionProvider create(final Set<Class<?>> templateFactories, final Class<?> factoryClass) {
        return new TemplateDefinitionProvider(
                List.of(customTemplateDefinitionDecorator),
                templateAvailabilityResolver,
                () -> Components.newInstance(ConfiguredAreaDefinition.class),
                templateFactories,
                () -> Components.newInstance(factoryClass),
                factoryClass
        );
    }
}

bind custom factory:

<component>
    <type>com.merkle.oss.magnolia.templatebuilder.TemplateDefinitionProvider.Factory</type>
    <implementation>com.somepackage.CustomTemplateDefinitionProviderFactory</implementation>
</component>

ParameterResolver

Implement and bind different AvailabilityParameterResolverFactory to customize injectable @Availabile method arguments.

import info.magnolia.rendering.template.TemplateDefinition;

import javax.inject.Inject;
import javax.jcr.Node;

import com.merkle.oss.magnolia.builder.parameter.ParameterResolver;
import com.merkle.oss.magnolia.powernode.PowerNodeService;
import com.merkle.oss.magnolia.templatebuilder.parameter.AvailabilityParameterResolverFactory;
import com.merkle.oss.magnolia.templatebuilder.parameter.DefaultAvailabilityParameterResolver;

public class CustomAvailabilityParamResolver extends ParameterResolver {

    public CustomAvailabilityParamResolver(
            final Node node,
            final TemplateDefinition templateDefinition
    ) {
        super(new DefaultAvailabilityParameterResolver(node, templateDefinition));
    }

    @Override
    public Object resolveParameter(final Class<?> parameterType) {
        if (parameterType.equals(SomeCustomParam.class)) {
            return new SomeCustomParam();
        }
        return super.resolveParameter(parameterType);
    }

    public static class Factory implements AvailabilityParameterResolverFactory {
        @Override
        public ParameterResolver create(final Node node, final TemplateDefinition templateDefinition) {
            return new CustomAvailabilityParamResolver(node, templateDefinition);
        }
    }
}
<component>
    <type>com.merkle.oss.magnolia.templatebuilder.parameter.AvailabilityParameterResolverFactory</type>
    <implementation>com.somepackage.CustomAvailabilityParamResolver$Factory</implementation>
</component>