diff --git a/modules/desktop/src/com/haulmont/cuba/desktop/sys/MenuBuilder.java b/modules/desktop/src/com/haulmont/cuba/desktop/sys/MenuBuilder.java index f26e5d5726..3c8ef1517c 100644 --- a/modules/desktop/src/com/haulmont/cuba/desktop/sys/MenuBuilder.java +++ b/modules/desktop/src/com/haulmont/cuba/desktop/sys/MenuBuilder.java @@ -20,9 +20,9 @@ package com.haulmont.cuba.desktop.sys; import com.haulmont.cuba.core.global.AppBeans; import com.haulmont.cuba.desktop.gui.components.DesktopComponentsHelper; import com.haulmont.cuba.desktop.sys.validation.ValidationAwareActionListener; -import com.haulmont.cuba.gui.NoSuchScreenException; import com.haulmont.cuba.gui.components.KeyCombination; -import com.haulmont.cuba.gui.config.*; +import com.haulmont.cuba.gui.config.MenuCommand; +import com.haulmont.cuba.gui.config.MenuConfig; import com.haulmont.cuba.gui.config.MenuItem; import com.haulmont.cuba.gui.logging.UserActionsLogger; import com.haulmont.cuba.security.global.UserSession; @@ -98,15 +98,7 @@ public class MenuBuilder { } private void assignCommand(final JMenuItem jMenuItem, MenuItem item) { - WindowConfig windowConfig = AppBeans.get(WindowConfig.NAME); - WindowInfo windowInfo; - try { - windowInfo = windowConfig.getWindowInfo(item.getId()); - } catch (NoSuchScreenException e) { - return; - } - - final MenuCommand command = new MenuCommand(item, windowInfo); + final MenuCommand command = new MenuCommand(item); jMenuItem.addActionListener(new ValidationAwareActionListener() { @Override public void actionPerformedAfterValidation(ActionEvent e) { @@ -114,7 +106,7 @@ public class MenuBuilder { StringBuilder menuPath = new StringBuilder(); formatMenuPath(item, menuPath); - userActionsLog.trace("Window {} was opened using menu item {}", windowInfo.getId(), menuPath.toString()); + userActionsLog.trace("Action \"{}\" was performed using menu item {}", command.getCommandDescription(), menuPath.toString()); } }); } diff --git a/modules/gui/src/com/haulmont/cuba/gui/config/MenuCommand.java b/modules/gui/src/com/haulmont/cuba/gui/config/MenuCommand.java index 7fbf0b8101..855ca905b5 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/config/MenuCommand.java +++ b/modules/gui/src/com/haulmont/cuba/gui/config/MenuCommand.java @@ -20,10 +20,7 @@ package com.haulmont.cuba.gui.config; import com.haulmont.bali.util.Dom4j; import com.haulmont.cuba.core.app.DataService; import com.haulmont.cuba.core.entity.Entity; -import com.haulmont.cuba.core.global.AppBeans; -import com.haulmont.cuba.core.global.EntityLoadInfo; -import com.haulmont.cuba.core.global.LoadContext; -import com.haulmont.cuba.core.global.Metadata; +import com.haulmont.cuba.core.global.*; import com.haulmont.cuba.core.sys.AppContext; import com.haulmont.cuba.gui.WindowManager; import com.haulmont.cuba.gui.WindowManager.OpenMode; @@ -32,115 +29,226 @@ import com.haulmont.cuba.gui.WindowManagerProvider; import com.haulmont.cuba.gui.WindowParams; import com.haulmont.cuba.gui.components.Window; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.reflect.MethodUtils; import org.dom4j.Element; import org.perf4j.StopWatch; import org.perf4j.log4j.Log4JStopWatch; +import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; public class MenuCommand { - private MenuItem item; - private WindowInfo windowInfo; + protected MenuItem item; - public MenuCommand(MenuItem item, WindowInfo windowInfo) { + protected MenuItemCommand command; + + public MenuCommand(MenuItem item) { this.item = item; - this.windowInfo = windowInfo; + + createCommand(); + } + + protected void createCommand() { + if (StringUtils.isNotEmpty(item.getScreen())) { + command = new ScreenCommand(item.getScreen(), item.getDescriptor()); + return; + } + + if (StringUtils.isNotEmpty(item.getRunnableClass())) { + command = new RunnableClassCommand(item.getRunnableClass()); + return; + } + + if (StringUtils.isNotEmpty(item.getBean())) { + command = new BeanCommand(item.getBean(), item.getBeanMethod()); + } } public void execute() { - StopWatch sw = new Log4JStopWatch("MenuItem." + windowInfo.getId()); + StopWatch sw = new Log4JStopWatch("MenuItem." + item.getId()); - Element descriptor = item.getDescriptor(); - Map params = loadParams(descriptor); - - OpenType openType = OpenType.NEW_TAB; - String openTypeStr = descriptor.attributeValue("openType"); - if (StringUtils.isNotEmpty(openTypeStr)) { - openType = OpenType.valueOf(openTypeStr); - } - - WindowManagerProvider wmProvider = AppBeans.get(WindowManagerProvider.NAME); - WindowManager wm = wmProvider.get(); - - if (openType.getOpenMode() == OpenMode.DIALOG) { - String resizable = descriptor.attributeValue("resizable"); - if (StringUtils.isNotEmpty(resizable)) { - openType = openType.resizable(Boolean.parseBoolean(resizable)); - } - } - - final String id = windowInfo.getId(); - if (id.endsWith(Window.CREATE_WINDOW_SUFFIX) || id.endsWith(Window.EDITOR_WINDOW_SUFFIX)) { - Entity entityItem; - if (params.containsKey("item")) { - entityItem = (Entity) params.get("item"); - } else { - final String[] strings = id.split("[.]"); - String metaClassName; - if (strings.length == 2) { - metaClassName = strings[0]; - } else if (strings.length == 3) { - metaClassName = strings[1]; - } else { - throw new UnsupportedOperationException(); - } - - Metadata metadata = AppBeans.get(Metadata.NAME); - entityItem = metadata.create(metaClassName); - } - wm.openEditor( - windowInfo, - entityItem, - openType, - params - ); - } else { - wm.openWindow( - windowInfo, - openType, - params - ); - } + command.run(); sw.stop(); } - private Map loadParams(Element descriptor) { - Map params = new HashMap<>(); - for (Element element : Dom4j.elements(descriptor, "param")) { - String value = element.attributeValue("value"); - EntityLoadInfo info = EntityLoadInfo.parse(value); - if (info == null) { - if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) { - Boolean booleanValue = Boolean.valueOf(value); - params.put(element.attributeValue("name"), booleanValue); - } else { - if (value.startsWith("${") && value.endsWith("}")) { - String property = AppContext.getProperty(value.substring(2, value.length() - 1)); - if (!StringUtils.isEmpty(property)) - value = property; - } - params.put(element.attributeValue("name"), value); + public String getCommandDescription() { + return command.getDescription(); + } + + protected interface MenuItemCommand extends Runnable { + + String getDescription(); + } + + protected class ScreenCommand implements MenuItemCommand { + + protected String screen; + protected Element descriptor; + + protected ScreenCommand(String screen, Element descriptor) { + this.screen = screen; + this.descriptor = descriptor; + } + + @Override + public void run() { + Map params = loadParams(descriptor); + + OpenType openType = OpenType.NEW_TAB; + String openTypeStr = descriptor.attributeValue("openType"); + if (StringUtils.isNotEmpty(openTypeStr)) { + openType = OpenType.valueOf(openTypeStr); + } + + WindowManager wm = AppBeans.get(WindowManagerProvider.class).get(); + + if (openType.getOpenMode() == OpenMode.DIALOG) { + String resizable = descriptor.attributeValue("resizable"); + if (StringUtils.isNotEmpty(resizable)) { + openType = openType.resizable(Boolean.parseBoolean(resizable)); } + } + + WindowInfo windowInfo = AppBeans.get(WindowConfig.class).getWindowInfo(screen); + + final String id = windowInfo.getId(); + if (id.endsWith(Window.CREATE_WINDOW_SUFFIX) || id.endsWith(Window.EDITOR_WINDOW_SUFFIX)) { + Entity entityItem; + if (params.containsKey("item")) { + entityItem = (Entity) params.get("item"); + } else { + final String[] strings = id.split("[.]"); + String metaClassName; + if (strings.length == 2) { + metaClassName = strings[0]; + } else if (strings.length == 3) { + metaClassName = strings[1]; + } else { + throw new UnsupportedOperationException(); + } + + entityItem = AppBeans.get(Metadata.class).create(metaClassName); + } + wm.openEditor( + windowInfo, + entityItem, + openType, + params + ); } else { - params.put(element.attributeValue("name"), loadEntityInstance(info)); + wm.openWindow( + windowInfo, + openType, + params + ); } } - String caption = MenuConfig.getMenuItemCaption(item.getId()); - WindowParams.CAPTION.set(params, caption); + @Override + public String getDescription() { + return String.format("Opening window: \"%s\"", screen); + } - return params; + protected Map loadParams(Element descriptor) { + Map params = new HashMap<>(); + for (Element element : Dom4j.elements(descriptor, "param")) { + String value = element.attributeValue("value"); + EntityLoadInfo info = EntityLoadInfo.parse(value); + if (info == null) { + if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) { + Boolean booleanValue = Boolean.valueOf(value); + params.put(element.attributeValue("name"), booleanValue); + } else { + if (value.startsWith("${") && value.endsWith("}")) { + String property = AppContext.getProperty(value.substring(2, value.length() - 1)); + if (!StringUtils.isEmpty(property)) + value = property; + } + params.put(element.attributeValue("name"), value); + } + } else { + params.put(element.attributeValue("name"), loadEntityInstance(info)); + } + } + + String caption = AppBeans.get(MenuConfig.class).getItemCaption(screen); + WindowParams.CAPTION.set(params, caption); + + return params; + } + + protected Entity loadEntityInstance(EntityLoadInfo info) { + LoadContext ctx = new LoadContext(info.getMetaClass()).setId(info.getId()); + if (info.getViewName() != null) { + ctx.setView(info.getViewName()); + } + + //noinspection unchecked + return AppBeans.get(DataService.class).load(ctx); + } } - private Entity loadEntityInstance(EntityLoadInfo info) { - DataService ds = AppBeans.get(DataService.NAME); - LoadContext ctx = new LoadContext(info.getMetaClass()).setId(info.getId()); - if (info.getViewName() != null) - ctx.setView(info.getViewName()); - Entity entity = ds.load(ctx); - return entity; + protected class BeanCommand implements MenuItemCommand { + + protected String bean; + protected String beanMethod; + + protected BeanCommand(String bean, String beanMethod) { + this.bean = bean; + this.beanMethod = beanMethod; + } + + @Override + public void run() { + Object beanInstance = AppBeans.get(bean); + + try { + MethodUtils.invokeMethod(beanInstance, beanMethod, null); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + public String getDescription() { + return String.format("Calling bean method: %s#%s", bean, beanMethod); + } + } + + protected class RunnableClassCommand implements MenuItemCommand { + + protected String runnableClass; + + protected RunnableClassCommand(String runnableClass) { + this.runnableClass = runnableClass; + } + + @Override + public void run() { + Class clazz = AppBeans.get(Scripting.class).loadClass(runnableClass); + if (clazz == null) { + throw new IllegalStateException(String.format("Can't load class: %s", runnableClass)); + } + + if (!Runnable.class.isAssignableFrom(clazz)) { + throw new IllegalStateException(String.format("%s is not instance of Runnable", runnableClass)); + } + + try { + Object runnableInstance = clazz.newInstance(); + + ((Runnable) runnableInstance).run(); + } catch (InstantiationException | IllegalAccessException e) { + throw new DevelopmentException(String.format("Failed to get a new instance of %s", runnableClass)); + } + } + + @Override + public String getDescription() { + return String.format("Running \"%s\"", runnableClass); + } } } \ No newline at end of file diff --git a/modules/gui/src/com/haulmont/cuba/gui/config/MenuConfig.java b/modules/gui/src/com/haulmont/cuba/gui/config/MenuConfig.java index 0fc0d9ddd6..8c3fffb74e 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/config/MenuConfig.java +++ b/modules/gui/src/com/haulmont/cuba/gui/config/MenuConfig.java @@ -65,8 +65,8 @@ public class MenuConfig { /** * Localized menu item caption. - * @param id screen ID as defined in screens.xml * + * @param id screen ID as defined in screens.xml * @deprecated Use {@link MenuConfig#getItemCaption(String)} */ @Deprecated @@ -180,14 +180,10 @@ public class MenuConfig { loadDescription(element, menuItem); loadMenuItems(element, menuItem); } else if ("item".equals(element.getName())) { - String id = element.attributeValue("id"); - if (!StringUtils.isBlank(id)) { - menuItem = new MenuItem(currentParentItem, id); - menuItem.setDescriptor(element); - loadIcon(element, menuItem); - loadShortcut(menuItem, element); - loadStylename(element, menuItem); - loadDescription(element, menuItem); + menuItem = createMenuItem(element, currentParentItem); + + if (menuItem == null) { + continue; } } else if ("separator".equals(element.getName())) { String id = element.attributeValue("id"); @@ -210,6 +206,69 @@ public class MenuConfig { } } + protected MenuItem createMenuItem(Element element, MenuItem currentParentItem) { + String id = element.attributeValue("id"); + + String idFromActions; + + String screen = element.attributeValue("screen"); + idFromActions = StringUtils.isNotEmpty(screen) ? screen : null; + + String runnableClass = element.attributeValue("class"); + checkDuplicateAction(idFromActions, runnableClass); + idFromActions = StringUtils.isNotEmpty(runnableClass) ? runnableClass : idFromActions; + + String bean = element.attributeValue("bean"); + String beanMethod = element.attributeValue("beanMethod"); + + if (StringUtils.isNotEmpty(bean) && StringUtils.isEmpty(beanMethod) || + StringUtils.isEmpty(bean) && StringUtils.isNotEmpty(beanMethod)) { + throw new IllegalStateException("Both bean and beanMethod should be defined."); + } + + checkDuplicateAction(idFromActions, bean, beanMethod); + + String fqn = bean + "#" + beanMethod; + idFromActions = StringUtils.isNotEmpty(bean) && StringUtils.isNotEmpty(beanMethod) ? fqn : idFromActions; + + if (StringUtils.isEmpty(id) && StringUtils.isEmpty(idFromActions)) { + throw new IllegalStateException("MenuItem should have at least one action"); + } + + if (StringUtils.isEmpty(id) && StringUtils.isNotEmpty(idFromActions)) { + id = idFromActions; + } + + if (StringUtils.isNotEmpty(id) && StringUtils.isEmpty(idFromActions)) { + screen = id; + } + + MenuItem menuItem = new MenuItem(currentParentItem, id); + + menuItem.setScreen(screen); + menuItem.setRunnableClass(runnableClass); + menuItem.setBean(bean); + menuItem.setBeanMethod(beanMethod); + + menuItem.setDescriptor(element); + loadIcon(element, menuItem); + loadShortcut(menuItem, element); + loadStylename(element, menuItem); + loadDescription(element, menuItem); + + return menuItem; + } + + protected void checkDuplicateAction(String menuItemId, String... actionDefinition) { + boolean actionDefined = true; + for (String s : actionDefinition) { + actionDefined &= StringUtils.isNotEmpty(s); + } + if (StringUtils.isNotEmpty(menuItemId) && actionDefined) { + throw new IllegalStateException("MenuItem can't have more than one action."); + } + } + protected void loadExpanded(Element element, MenuItem menuItem) { String expanded = element.attributeValue("expanded"); if (StringUtils.isNotEmpty(expanded)) { diff --git a/modules/gui/src/com/haulmont/cuba/gui/config/MenuItem.java b/modules/gui/src/com/haulmont/cuba/gui/config/MenuItem.java index e1b8e1401c..7d3583d068 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/config/MenuItem.java +++ b/modules/gui/src/com/haulmont/cuba/gui/config/MenuItem.java @@ -35,6 +35,10 @@ public class MenuItem { private List children = new ArrayList<>(); private String id; + private String screen; + private String runnableClass; + private String bean; + private String beanMethod; private String stylename; private String icon; private String description; @@ -152,4 +156,36 @@ public class MenuItem { public void setExpanded(boolean expanded) { this.expanded = expanded; } + + public String getScreen() { + return screen; + } + + public void setScreen(String screen) { + this.screen = screen; + } + + public String getRunnableClass() { + return runnableClass; + } + + public void setRunnableClass(String runnableClass) { + this.runnableClass = runnableClass; + } + + public String getBean() { + return bean; + } + + public void setBean(String bean) { + this.bean = bean; + } + + public String getBeanMethod() { + return beanMethod; + } + + public void setBeanMethod(String beanMethod) { + this.beanMethod = beanMethod; + } } \ No newline at end of file diff --git a/modules/gui/src/com/haulmont/cuba/gui/menu.xsd b/modules/gui/src/com/haulmont/cuba/gui/menu.xsd index ccdea9a4a4..ffa72abc2e 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/menu.xsd +++ b/modules/gui/src/com/haulmont/cuba/gui/menu.xsd @@ -56,6 +56,10 @@ + + + + diff --git a/modules/web/src/com/haulmont/cuba/web/sys/MenuBuilder.java b/modules/web/src/com/haulmont/cuba/web/sys/MenuBuilder.java index bb10f74045..eacc05b04d 100644 --- a/modules/web/src/com/haulmont/cuba/web/sys/MenuBuilder.java +++ b/modules/web/src/com/haulmont/cuba/web/sys/MenuBuilder.java @@ -16,13 +16,15 @@ package com.haulmont.cuba.web.sys; -import com.haulmont.cuba.core.global.DevelopmentException; +import com.haulmont.cuba.core.global.Scripting; import com.haulmont.cuba.gui.ComponentsHelper; -import com.haulmont.cuba.gui.NoSuchScreenException; import com.haulmont.cuba.gui.components.KeyCombination; import com.haulmont.cuba.gui.components.Window; import com.haulmont.cuba.gui.components.mainwindow.AppMenu; -import com.haulmont.cuba.gui.config.*; +import com.haulmont.cuba.gui.config.MenuCommand; +import com.haulmont.cuba.gui.config.MenuConfig; +import com.haulmont.cuba.gui.config.MenuItem; +import com.haulmont.cuba.gui.config.WindowConfig; import com.haulmont.cuba.security.global.UserSession; import com.haulmont.cuba.web.toolkit.MenuShortcutAction; import com.vaadin.event.ShortcutListener; @@ -58,6 +60,9 @@ public class MenuBuilder { @Inject protected WindowConfig windowConfig; + @Inject + protected Scripting scripting; + protected AppMenu appMenu; public MenuBuilder() { @@ -172,34 +177,10 @@ public class MenuBuilder { if (CollectionUtils.isNotEmpty(item.getChildren()) || item.isMenu()) //check item is menu return null; - WindowInfo windowInfo = null; - if (!item.isSeparator()) { - try { - windowInfo = windowConfig.getWindowInfo(item.getId()); - } catch (NoSuchScreenException e) { - log.error("Invalid screen ID for menu item: " + item.getId()); - } - } + MenuCommand menuCommand = new MenuCommand(item); - final MenuCommand command; - if (windowInfo != null) { - command = new MenuCommand(item, windowInfo); - } else { - command = null; - } - - return selectedItem -> { - if (command != null) { - command.execute(); - } else { - if (item.getParent() != null) { - throw new DevelopmentException("Invalid screen ID for menu item: " + item.getId(), - "Parent menu ID", item.getParent().getId()); - } else { - throw new DevelopmentException("Invalid screen ID for menu item: " + item.getId()); - } - } - }; + return menuItem -> + menuCommand.execute(); } protected boolean isMenuItemEmpty(AppMenu.MenuItem menuItem) { diff --git a/modules/web/src/com/haulmont/cuba/web/sys/SideMenuBuilder.java b/modules/web/src/com/haulmont/cuba/web/sys/SideMenuBuilder.java index 3122c52837..ce95c1752b 100644 --- a/modules/web/src/com/haulmont/cuba/web/sys/SideMenuBuilder.java +++ b/modules/web/src/com/haulmont/cuba/web/sys/SideMenuBuilder.java @@ -17,13 +17,14 @@ package com.haulmont.cuba.web.sys; import com.google.common.base.Strings; -import com.haulmont.cuba.core.global.DevelopmentException; import com.haulmont.cuba.gui.ComponentsHelper; -import com.haulmont.cuba.gui.NoSuchScreenException; import com.haulmont.cuba.gui.components.KeyCombination; import com.haulmont.cuba.gui.components.Window; import com.haulmont.cuba.gui.components.mainwindow.SideMenu; -import com.haulmont.cuba.gui.config.*; +import com.haulmont.cuba.gui.config.MenuCommand; +import com.haulmont.cuba.gui.config.MenuConfig; +import com.haulmont.cuba.gui.config.MenuItem; +import com.haulmont.cuba.gui.config.WindowConfig; import com.haulmont.cuba.security.global.UserSession; import com.vaadin.event.ShortcutListener; import com.vaadin.ui.AbstractComponent; @@ -171,32 +172,10 @@ public class SideMenuBuilder { if (!item.getChildren().isEmpty() || item.isMenu()) //check item is menu return null; - WindowInfo windowInfo = null; - try { - windowInfo = windowConfig.getWindowInfo(item.getId()); - } catch (NoSuchScreenException e) { - log.error("Invalid screen ID for menu item: " + item.getId()); - } + MenuCommand command = new MenuCommand(item); - final MenuCommand command; - if (windowInfo != null) { - command = new MenuCommand(item, windowInfo); - } else { - command = null; - } - - return event -> { - if (command != null) { + return event -> command.execute(); - } else { - if (item.getParent() != null) { - throw new DevelopmentException("Invalid screen ID for menu item: " + item.getId(), - "Parent menu ID", item.getParent().getId()); - } else { - throw new DevelopmentException("Invalid screen ID for menu item: " + item.getId()); - } - } - }; } protected boolean isMenuItemEmpty(SideMenu.MenuItem menuItem) {