Skip to content

Commit

Permalink
Merge pull request #10 from merkle-open/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
michaelrauch authored Feb 7, 2024
2 parents 37cda6a + 5534aac commit 04963a8
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 58 deletions.
21 changes: 2 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,6 @@ The position of the app in the group can be configured with the 'order' annotati
The AppFactory requires at least one method marked with the `@SubApp` annotation
this method must return a `info.magnolia.ui.api.app.SubAppDescriptor`.

#### ChooseDialog, optional (Target: Method)
A method marked with `@ChooseDialog` must return a `info.magnolia.ui.dialog.definition.ChooseDialogDefinition`.

#### AppPermissions, optional (Target: Method)
A method marked with `@AppPermissions` must return a `info.magnolia.cms.security.operations.AccessDefinition`.

Expand Down Expand Up @@ -122,7 +119,8 @@ import info.magnolia.ui.workbench.column.definition.ColumnDefinition;
id = SampleApp.ID,
name = SampleApp.NAME,
label = SampleApp.NAME,
icon = MgnlIcon.TAG_2_APP
icon = MgnlIcon.TAG_2_APP,
generateLegacyChooserApp = true
)
public class SampleApp {
public static final String NAME = "SampleApp";
Expand All @@ -149,21 +147,6 @@ public class SampleApp {
.width(160)
};

// optional - if not specified the chooser dialog will contain the same columns as the app (columnDefinitions)
@ChooseDialog
public ChooseDialogDefinition getChooseDialog() {
return new ChooseDialogBuilder().contentConnector(
new JcrContentConnectorBuilder()
.workspace("<WORKSPACE>")
.defaultOrder("jcrName")
.rootPath("/")
.nodeTypes(
new NodeTypeBuilder()
.name("<NODE_TYPE>")
.icon(MgnlIcon.OPEN_NEW_WINDOW)
));
}

@SubApp
public SubAppDescriptor getBrowser() {
return new BrowserAppBuilder<>()
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>com.namics.oss.magnolia</groupId>
<artifactId>magnolia-appbuilder</artifactId>
<version>1.1.10</version>
<version>1.1.11-SNAPSHOT</version>
<packaging>jar</packaging>

<name>${project.artifactId}</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,42 @@
import org.springframework.aop.support.AopUtils;

public class AppDefinitionMetaDataBuilder extends DefinitionMetadataBuilder {
private final Object appFactory;
private final String name;

public AppDefinitionMetaDataBuilder(final Object appFactory) {
this.appFactory = appFactory;
final AppFactory appFactoryAnnotation = AopUtils
.getTargetClass(appFactory)
.getAnnotation(AppFactory.class);

this.name = generateName(appFactoryAnnotation);
type(DefinitionTypes.APP);
location(appFactory.getClass().getName());
location(generateLocation(appFactory));
module(generateModule(appFactoryAnnotation));
relativeLocation(generateRelativeLocation(appFactoryAnnotation));
name(name);
}

@Override
protected String buildReferenceId() {
final AppFactory appFactoryAnnotation = AopUtils
.getTargetClass(appFactory)
.getAnnotation(AppFactory.class);
final String id = appFactoryAnnotation.id();
final String module = id.contains(":") ? StringUtils.substringBefore(id, ":") : null;
final String relativeLocation = id.contains(":") ? StringUtils.substringAfter(id, ":") : id;
final String name = relativeLocation.indexOf('/') != -1 ? StringUtils.substringAfterLast(relativeLocation, "/") : relativeLocation;
module(module).relativeLocation(relativeLocation).name(name);
return name;
}

protected String generateLocation(final Object appFactory) {
return appFactory.getClass().getName();
}

protected String generateModule(final AppFactory appFactoryAnnotation) {
final String id = appFactoryAnnotation.id();
return id.contains(":") ? StringUtils.substringBefore(id, ":") : null;
}

protected String generateRelativeLocation(final AppFactory appFactoryAnnotation) {
final String id = appFactoryAnnotation.id();
return id.contains(":") ? StringUtils.substringAfter(id, ":") : id;
}

protected String generateName(final AppFactory appFactoryAnnotation) {
final String relativeLocation = generateRelativeLocation(appFactoryAnnotation);
return relativeLocation.indexOf('/') != -1 ? StringUtils.substringAfterLast(relativeLocation, "/") : relativeLocation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ protected void onBeanDetection(final Object appFactory, final String beanName) {
LOG.info("Detected app bean with name '{}'", beanName);
// build app descriptor from detected factory bean
final AppDescriptorProvider appDescriptorProvider = new AppDescriptorProvider(appFactory);
final LegacyAppDescriptorProvider legacyAppDescriptorProvider = new LegacyAppDescriptorProvider(appFactory);
// register app descriptor
if(legacyAppDescriptorProvider.shouldRegister()) {
LOG.info("Registered legacy chooser app '{}'", legacyAppDescriptorProvider.getMetadata().getName());
appDescriptorRegistry.register(legacyAppDescriptorProvider);
}
appDescriptorRegistry.register(appDescriptorProvider);
LOG.info("Registered app '{}'", appDescriptorProvider.getMetadata().getName());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package com.namics.oss.magnolia.appbuilder;

import com.google.common.collect.ImmutableList;
import com.namics.oss.magnolia.appbuilder.annotations.AppFactory;
import info.magnolia.config.registry.DefinitionMetadata;
import info.magnolia.config.registry.Registry;
import info.magnolia.ui.api.app.AppDescriptor;
import info.magnolia.ui.api.app.SubAppDescriptor;
import info.magnolia.ui.contentapp.ConfiguredContentAppDescriptor;
import info.magnolia.ui.contentapp.ContentApp;
import info.magnolia.ui.contentapp.browser.BrowserSubAppDescriptor;
import info.magnolia.ui.contentapp.browser.ConfiguredBrowserSubAppDescriptor;
import info.magnolia.ui.contentapp.column.jcr.JcrTitleColumnDefinition;
import info.magnolia.ui.contentapp.configuration.BrowserDescriptor;
import info.magnolia.ui.contentapp.configuration.ContentViewDefinition;
import info.magnolia.ui.contentapp.configuration.TreeViewDefinition;
import info.magnolia.ui.contentapp.configuration.WorkbenchDefinition;
import info.magnolia.ui.contentapp.configuration.column.ColumnDefinition;
import info.magnolia.ui.datasource.jcr.JcrDatasourceDefinition;
import info.magnolia.ui.vaadin.integration.contentconnector.ConfiguredJcrContentConnectorDefinition;
import info.magnolia.ui.vaadin.integration.contentconnector.ConfiguredNodeTypeDefinition;
import info.magnolia.ui.vaadin.integration.contentconnector.JcrContentConnectorDefinition;
import info.magnolia.ui.vaadin.integration.jcr.ModelConstants;
import info.magnolia.ui.workbench.column.definition.PropertyColumnDefinition;
import info.magnolia.ui.workbench.definition.ConfiguredWorkbenchDefinition;
import info.magnolia.ui.workbench.tree.TreePresenterDefinition;
import org.springframework.aop.support.AopUtils;

import java.util.*;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Provides legacy/UI5 appDescriptor to be able to open a legacy/UI5 chooser dialog to content from an UI6 app.
* <br>
* See <a href="https://docs.magnolia-cms.com/product-docs/6.2/Support/Magnolia-5-UI-documentation/Dialog-definition-5-UI/Field-definition-5-UI/List-of-fields-5-UI/Link-field-5-UI.html">appName description warning</a>
*/
public class LegacyAppDescriptorProvider extends AppDescriptorProvider {
public static final UnaryOperator<String> CONVERT_APP_NAME = appName -> appName + "-legacyChooser";
private final DefinitionMetadata metadata;
private final boolean shouldRegister;

public LegacyAppDescriptorProvider(final Object appFactory) {
super(appFactory);
final AppFactory appFactoryAnnotation = AopUtils
.getTargetClass(appFactory)
.getAnnotation(AppFactory.class);
this.shouldRegister = appFactoryAnnotation.generateLegacyChooserApp();
this.metadata = new LegacyAppDefinitionMetaDataBuilder(appFactory).build();
}

boolean shouldRegister() {
return shouldRegister;
}

@Override
public AppDescriptor get() throws Registry.InvalidDefinitionException {
final AppDescriptor appDescriptor = super.get();
final ConfiguredContentAppDescriptor legacyAppDescriptor = new ConfiguredContentAppDescriptor();
legacyAppDescriptor.setName(CONVERT_APP_NAME.apply(appDescriptor.getName()));
legacyAppDescriptor.setAppClass(ContentApp.class);
legacyAppDescriptor.setEnabled(false);
legacyAppDescriptor.setSubApps(getBrowserSubApps(appDescriptor));
return legacyAppDescriptor;
}

@Override
public DefinitionMetadata getMetadata() {
return metadata;
}

private Map<String, SubAppDescriptor> getBrowserSubApps(final AppDescriptor appDescriptor) {
return appDescriptor.getSubApps().values().stream()
.filter(BrowserDescriptor.class::isInstance)
.map(BrowserDescriptor.class::cast)
.map(browserDescriptor -> convert(appDescriptor, browserDescriptor))
.collect(Collectors.toMap(SubAppDescriptor::getName, Function.identity()));
}

private BrowserSubAppDescriptor convert(final AppDescriptor appDescriptor, final BrowserDescriptor browserDescriptor) {
final ConfiguredBrowserSubAppDescriptor legacyBrowserDescriptor = new ConfiguredBrowserSubAppDescriptor();
legacyBrowserDescriptor.setName(browserDescriptor.getName());
legacyBrowserDescriptor.setIcon(browserDescriptor.getIcon());
legacyBrowserDescriptor.setLabel(browserDescriptor.getLabel());
legacyBrowserDescriptor.setContentConnector(convert(
(JcrDatasourceDefinition) browserDescriptor.getDatasource(),
getIcons(browserDescriptor.getWorkbench())
));
legacyBrowserDescriptor.setWorkbench(convert(appDescriptor, browserDescriptor, browserDescriptor.getWorkbench()));
return legacyBrowserDescriptor;
}

private JcrContentConnectorDefinition convert(final JcrDatasourceDefinition jcrDatasourceDefinition, final Map<String, String> icons) {
final ConfiguredJcrContentConnectorDefinition legacyJcrContentConnectorDefinition = new ConfiguredJcrContentConnectorDefinition();
legacyJcrContentConnectorDefinition.setWorkspace(jcrDatasourceDefinition.getWorkspace());
legacyJcrContentConnectorDefinition.setNodeTypes(
jcrDatasourceDefinition.getAllowedNodeTypes().stream().map(nodeType -> {
final ConfiguredNodeTypeDefinition nodeTypeDefinition = new ConfiguredNodeTypeDefinition();
nodeTypeDefinition.setName(nodeType);
nodeTypeDefinition.setIcon(icons.get(nodeType));
return nodeTypeDefinition;
}).collect(Collectors.toList())
);
return legacyJcrContentConnectorDefinition;
}

private Map<String, String> getIcons(final WorkbenchDefinition workbench) {
return streamTreeViewColumns(workbench)
.filter(JcrTitleColumnDefinition.class::isInstance)
.map(JcrTitleColumnDefinition.class::cast)
.map(JcrTitleColumnDefinition::getNodeTypeToIcon)
.findFirst()
.orElseGet(Collections::emptyMap);
}

private Stream<ColumnDefinition> streamTreeViewColumns(final WorkbenchDefinition workbench) {
return ((List<ContentViewDefinition>) workbench.getContentViews())
.stream()
.filter(TreeViewDefinition.class::isInstance)
.map(TreeViewDefinition.class::cast)
.map(view -> (List<ColumnDefinition>) view.getColumns())
.flatMap(Collection::stream);
}

private info.magnolia.ui.workbench.definition.WorkbenchDefinition convert(
final AppDescriptor appDescriptor,
final BrowserDescriptor browserDescriptor,
final WorkbenchDefinition workbench
) {
final ConfiguredWorkbenchDefinition legacyWorkbench = new ConfiguredWorkbenchDefinition();
legacyWorkbench.setName(workbench.getName());
legacyWorkbench.setDialogWorkbench(true);
legacyWorkbench.setContentViews(List.of(getContentView(appDescriptor, browserDescriptor, workbench)));
return legacyWorkbench;
}

protected TreePresenterDefinition getContentView(
final AppDescriptor appDescriptor,
final BrowserDescriptor browserDescriptor,
final WorkbenchDefinition workbench
) {
final PropertyColumnDefinition nameColumn = new PropertyColumnDefinition();
nameColumn.setName("name");
nameColumn.setPropertyName(ModelConstants.JCR_NAME);

final TreePresenterDefinition treePresenter = new TreePresenterDefinition();
final List<info.magnolia.ui.workbench.column.definition.ColumnDefinition> simpleColumns = getSimpleColumns(appDescriptor, browserDescriptor, workbench);
final ImmutableList.Builder<info.magnolia.ui.workbench.column.definition.ColumnDefinition> columns = ImmutableList.builder();
if(simpleColumns.stream().noneMatch(column -> Objects.equals(column.getName(), nameColumn.getName()))) {
//add name column on first place if missing
columns.add(nameColumn);
}
columns.addAll(simpleColumns);
treePresenter.setColumns(columns.build());
return treePresenter;
}

private List<info.magnolia.ui.workbench.column.definition.ColumnDefinition> getSimpleColumns(
final AppDescriptor appDescriptor,
final BrowserDescriptor browserDescriptor,
final WorkbenchDefinition workbench
) {
return streamTreeViewColumns(workbench)
.filter(column -> column.getValueProvider() == null)
.map(column ->
convert(appDescriptor, browserDescriptor, column)
)
.collect(Collectors.toList());
}

private PropertyColumnDefinition convert(
final AppDescriptor appDescriptor,
final BrowserDescriptor browserDescriptor,
final ColumnDefinition column
) {
final PropertyColumnDefinition legacyColumn = new PropertyColumnDefinition();
legacyColumn.setName(column.getName());
legacyColumn.setWidth((int) column.getWidth());
legacyColumn.setExpandRatio(column.getExpandRatio());
legacyColumn.setLabel(appDescriptor.getName() + "." + browserDescriptor.getName() + ".views." + column.getName() + ".label");
legacyColumn.setSortable(column.isSortable());
legacyColumn.setPropertyName(column.getName());
return legacyColumn;
}

private static final class LegacyAppDefinitionMetaDataBuilder extends AppDefinitionMetaDataBuilder {
public LegacyAppDefinitionMetaDataBuilder(final Object appFactory) {
super(appFactory);
}

@Override
protected String generateName(final AppFactory appFactoryAnnotation) {
return CONVERT_APP_NAME.apply(super.generateName(appFactoryAnnotation));
}

@Override
protected String generateLocation(final Object appFactory) {
return CONVERT_APP_NAME.apply(super.generateLocation(appFactory));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void execute() {
final String nodeName = nodeNameHelper.getUniqueName(
parent,
getForm().getPropertyValue(definition.getNodeNameProperty()).map(String::valueOf).orElseThrow(() ->
new ActionExecutionException("Failed to get node name property " + definition.getNodeNameProperty() + "from form")
new ActionExecutionException("Failed to get node name property " + definition.getNodeNameProperty() + " from form")
)
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,5 @@

boolean isEnabled() default true;

/**
* Leave empty to use admincentral/config.yaml
*/
String launcherGroup() default "";

/**
* Leave empty to use admincentral/config.yaml
*/
int order() default Integer.MAX_VALUE;
boolean generateLegacyChooserApp() default false;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.namics.oss.magnolia.appbuilder.formatter;

import info.magnolia.ui.contentapp.configuration.column.ConfiguredColumnDefinition;

import javax.jcr.Item;

public class PropertyNameColumnDefinition extends ConfiguredColumnDefinition<Item> {
private final String propertyName;

public PropertyNameColumnDefinition(final String name, final String propertyName) {
this.propertyName = propertyName;
setName(name);
setValueProvider(PropertyNameValueProvider.class);
}

public String getPropertyName() {
return propertyName;
}
}
Loading

0 comments on commit 04963a8

Please sign in to comment.