Skip to content

The AppBuilder module is a builder for Magnolia apps in a Blossom context comparable to the Blossom DialogBuilder. Instead of using YAML, it allows to define apps in Java which is less error prone than using YAML, especially for big apps

Notifications You must be signed in to change notification settings

merkle-open/magnolia-appbuilder

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Magnolia Appbuilder

The AppBuilder module is a builder for Magnolia apps in a java comparable to Blossom DialogBuilder.

Requirements

  • Java 11
  • Magnolia >= 6.0

Setup

Add Maven dependency:

<dependency>
    <groupId>com.namics.oss.magnolia</groupId>
    <artifactId>magnolia-appbuilder</artifactId>
    <version>1.2.1</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 org.apache.commons.lang3.reflect.TypeLiteral;
import org.reflections.Reflections;

import com.google.inject.multibindings.Multibinder;
import com.namics.oss.magnolia.appbuilder.AppRegistrar;
import com.namics.oss.magnolia.appbuilder.ChooserDialogRegistrar;
import com.namics.oss.magnolia.appbuilder.annotations.AppFactory;
import com.namics.oss.magnolia.appbuilder.annotations.ChooserDialogFactory;

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<?>> appFactoryMultibinder = Multibinder.newSetBinder(binder(), new TypeLiteral<>() {}, AppRegistrar.AppFactories.class);
        new Reflections(getClass()).getTypesAnnotatedWith(AppFactory.class).forEach(clazz -> appFactoryMultibinder.addBinding().toInstance(clazz));

        final Multibinder<Class<?>> chooserDialogFactoryMultibinder = Multibinder.newSetBinder(binder(), new TypeLiteral<>() {}, ChooserDialogRegistrar.ChooserDialogFactories.class);
        new Reflections(getClass()).getTypesAnnotatedWith(ChooserDialogFactory.class).forEach(clazz -> chooserDialogFactoryMultibinder.addBinding().toInstance(clazz));
    }
}

How to use

AppFactory

To create a new app, add a class with the @AppFactory annotation and at least one method annotated with @SubApp returning a info.magnolia.ui.api.app.SubAppDescriptor. Make sure the the class in in a package which is scanned for @AppFactorys.

ChooserDialog

By default Magnolia generates chooser dialogs with the workbench of the default supapp (See AppAwareWorkbenchChooserDefinition).

Examples

The following class is a demo app, made with the AppBuilder:

AppFactory

import com.namics.oss.magnolia.appbuilder.MgnlIcon;
import com.namics.oss.magnolia.appbuilder.action.AppActionDefinitions;
import com.namics.oss.magnolia.appbuilder.action.AppActionGroupDefinition;
import com.namics.oss.magnolia.appbuilder.action.add.AddAppActionDefinition;
import com.namics.oss.magnolia.appbuilder.action.edit.EditAppActionDefinition;
import com.namics.oss.magnolia.appbuilder.annotations.AppFactory;
import com.namics.oss.magnolia.appbuilder.annotations.SubApp;
import com.namics.oss.magnolia.appbuilder.builder.BrowserAppBuilder;
import com.namics.oss.magnolia.appbuilder.builder.ColumnDefinitionBuilder;

import info.magnolia.jcr.util.NodeTypes;
import info.magnolia.ui.api.app.SubAppDescriptor;
import info.magnolia.ui.contentapp.configuration.column.ColumnDefinition;
import info.magnolia.ui.datasource.jcr.JcrDatasourceDefinition;

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

import javax.jcr.Item;

@AppFactory(
        id = SampleApp.ID,
        name = SampleApp.NAME,
        label = SampleApp.NAME,
        icon = MgnlIcon.TAG_2_APP
)
public class SampleApp {
    public static final String NAME = "SampleApp";
    public static final String ID = "module:apps/" + NAME;

    private static final Map<String, String> NODE_TYPES_ICONS = Map.of(
            NodeTypes.Folder.NAME, MgnlIcon.FOLDER
    );

    private final List<ColumnDefinition<Item>> columns = List.of(
            ColumnDefinitionBuilder.title(NODE_TYPES_ICONS),
            ColumnDefinitionBuilder.status(),
            ColumnDefinitionBuilder.modification()
    );

    @SubApp
    public SubAppDescriptor getBrowser() {
        return new BrowserAppBuilder<Item, JcrDatasourceDefinition>()
                .icon(MgnlIcon.TAG_2_APP)
                .columns(columns)
                .rootActions(
                        new AppActionGroupDefinition("addingActions", AddAppActionDefinition.FOLDER),
                        new AppActionGroupDefinition("activationActions", AppActionDefinitions.ACTIVATION),
                        new AppActionGroupDefinition("importExportActions", AppActionDefinitions.IMPORT_EXPORT)
                )
                .nodeActions(
                        NodeTypes.Folder.NAME,
                        EditAppActionDefinition.FOLDER,
                        new AppActionGroupDefinition("editActions", AppActionDefinitions.editActions(EditAppActionDefinition.FOLDER)),
                        new AppActionGroupDefinition("activationActions", AppActionDefinitions.ACTIVATION),
                        new AppActionGroupDefinition("importExportActions", AppActionDefinitions.IMPORT_EXPORT)
                )
                .build("<WORKSPACE>", Set.of(NodeTypes.Folder.NAME));
    }
}

ChooserDialog sample:

import com.namics.oss.magnolia.appbuilder.annotations.ChooserDialogFactory;
import com.namics.oss.magnolia.appbuilder.builder.ColumnDefinitionBuilder;
import info.magnolia.ui.contentapp.configuration.ContentViewDefinition;
import info.magnolia.ui.contentapp.configuration.ListViewDefinition;
import info.magnolia.ui.contentapp.configuration.TreeViewDefinition;
import info.magnolia.ui.contentapp.configuration.column.ColumnDefinition;

import javax.jcr.Item;
import java.util.List;

@ChooserDialogFactory(
        id = SampleChooserDialog.ID,
        label = SampleChooserDialog.NAME + ".title. label"
)
public class SampleChooserDialog {
    public static final String NAME = "SampleChooserDialog";
    public static final String ID = "SomeApp:dialogs/" + NAME;

    private final List<ColumnDefinition<Item>> columns = List.of(
            new ColumnDefinitionBuilder<Item>().build("someField")
    );

    public ContentViewDefinition<Item> tree() {
        final TreeViewDefinition<Item> tree = new TreeViewDefinition<>();
        tree.setName("tree");
        tree.setColumns(columns);
        return tree;
    }

    public ContentViewDefinition<Item> list() {
        final ListViewDefinition<Item> list = new ListViewDefinition<>();
        list.setName("list");
        list.setColumns(columns);
        return list;
    }
}
final LinkFieldDefinition<Node> definition = new LinkFieldDefinition<>();
definition.setChooserId(SampleChooserDialog.ID);
...

ValueProvider sample:

import com.namics.oss.magnolia.appbuilder.formatter.AbstractValueProvider;
import com.namics.oss.magnolia.powernode.PowerNodeService;

import javax.jcr.Node;
import javax.inject.Inject;
import java.util.Optional;

public class SampleValueProvider extends AbstractValueProvider {

	@Inject
	public SampleValueProvider(
			final PowerNodeService powerNodeService
    ) {
		super(powerNodeService, definition);
	}

	@Override
	protected Optional<String> getValue(final Node item) {
		final PowerNode powerNode = powerNodeService.convertToPowerNode(item);
		if (powerNode.isNodeType("<SOME_NODE_TYPE>")) {
			return powerNode.getPropertyValue("<SOME_FIELD>", String.class);
		}
		return Optional.empty();
	}
}

About

The AppBuilder module is a builder for Magnolia apps in a Blossom context comparable to the Blossom DialogBuilder. Instead of using YAML, it allows to define apps in Java which is less error prone than using YAML, especially for big apps

Resources

Stars

Watchers

Forks

Packages

No packages published