PL-8745 Ability to use the same screen in menu twice

This commit is contained in:
Daniil Tsarev 2017-03-31 20:45:29 +04:00
parent 6ed7ee38a4
commit abe1ab2e57
7 changed files with 324 additions and 165 deletions

View File

@ -20,9 +20,9 @@ package com.haulmont.cuba.desktop.sys;
import com.haulmont.cuba.core.global.AppBeans; import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.desktop.gui.components.DesktopComponentsHelper; import com.haulmont.cuba.desktop.gui.components.DesktopComponentsHelper;
import com.haulmont.cuba.desktop.sys.validation.ValidationAwareActionListener; 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.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.config.MenuItem;
import com.haulmont.cuba.gui.logging.UserActionsLogger; import com.haulmont.cuba.gui.logging.UserActionsLogger;
import com.haulmont.cuba.security.global.UserSession; import com.haulmont.cuba.security.global.UserSession;
@ -98,15 +98,7 @@ public class MenuBuilder {
} }
private void assignCommand(final JMenuItem jMenuItem, MenuItem item) { private void assignCommand(final JMenuItem jMenuItem, MenuItem item) {
WindowConfig windowConfig = AppBeans.get(WindowConfig.NAME); final MenuCommand command = new MenuCommand(item);
WindowInfo windowInfo;
try {
windowInfo = windowConfig.getWindowInfo(item.getId());
} catch (NoSuchScreenException e) {
return;
}
final MenuCommand command = new MenuCommand(item, windowInfo);
jMenuItem.addActionListener(new ValidationAwareActionListener() { jMenuItem.addActionListener(new ValidationAwareActionListener() {
@Override @Override
public void actionPerformedAfterValidation(ActionEvent e) { public void actionPerformedAfterValidation(ActionEvent e) {
@ -114,7 +106,7 @@ public class MenuBuilder {
StringBuilder menuPath = new StringBuilder(); StringBuilder menuPath = new StringBuilder();
formatMenuPath(item, menuPath); 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());
} }
}); });
} }

View File

@ -20,10 +20,7 @@ package com.haulmont.cuba.gui.config;
import com.haulmont.bali.util.Dom4j; import com.haulmont.bali.util.Dom4j;
import com.haulmont.cuba.core.app.DataService; import com.haulmont.cuba.core.app.DataService;
import com.haulmont.cuba.core.entity.Entity; import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.AppBeans; import com.haulmont.cuba.core.global.*;
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.sys.AppContext; import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.gui.WindowManager; import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.WindowManager.OpenMode; import com.haulmont.cuba.gui.WindowManager.OpenMode;
@ -32,27 +29,72 @@ import com.haulmont.cuba.gui.WindowManagerProvider;
import com.haulmont.cuba.gui.WindowParams; import com.haulmont.cuba.gui.WindowParams;
import com.haulmont.cuba.gui.components.Window; import com.haulmont.cuba.gui.components.Window;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.reflect.MethodUtils;
import org.dom4j.Element; import org.dom4j.Element;
import org.perf4j.StopWatch; import org.perf4j.StopWatch;
import org.perf4j.log4j.Log4JStopWatch; import org.perf4j.log4j.Log4JStopWatch;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class MenuCommand { public class MenuCommand {
private MenuItem item; protected MenuItem item;
private WindowInfo windowInfo;
public MenuCommand(MenuItem item, WindowInfo windowInfo) { protected MenuItemCommand command;
public MenuCommand(MenuItem item) {
this.item = 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() { public void execute() {
StopWatch sw = new Log4JStopWatch("MenuItem." + windowInfo.getId()); StopWatch sw = new Log4JStopWatch("MenuItem." + item.getId());
Element descriptor = item.getDescriptor(); command.run();
sw.stop();
}
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<String, Object> params = loadParams(descriptor); Map<String, Object> params = loadParams(descriptor);
OpenType openType = OpenType.NEW_TAB; OpenType openType = OpenType.NEW_TAB;
@ -61,8 +103,7 @@ public class MenuCommand {
openType = OpenType.valueOf(openTypeStr); openType = OpenType.valueOf(openTypeStr);
} }
WindowManagerProvider wmProvider = AppBeans.get(WindowManagerProvider.NAME); WindowManager wm = AppBeans.get(WindowManagerProvider.class).get();
WindowManager wm = wmProvider.get();
if (openType.getOpenMode() == OpenMode.DIALOG) { if (openType.getOpenMode() == OpenMode.DIALOG) {
String resizable = descriptor.attributeValue("resizable"); String resizable = descriptor.attributeValue("resizable");
@ -71,6 +112,8 @@ public class MenuCommand {
} }
} }
WindowInfo windowInfo = AppBeans.get(WindowConfig.class).getWindowInfo(screen);
final String id = windowInfo.getId(); final String id = windowInfo.getId();
if (id.endsWith(Window.CREATE_WINDOW_SUFFIX) || id.endsWith(Window.EDITOR_WINDOW_SUFFIX)) { if (id.endsWith(Window.CREATE_WINDOW_SUFFIX) || id.endsWith(Window.EDITOR_WINDOW_SUFFIX)) {
Entity entityItem; Entity entityItem;
@ -87,8 +130,7 @@ public class MenuCommand {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
Metadata metadata = AppBeans.get(Metadata.NAME); entityItem = AppBeans.get(Metadata.class).create(metaClassName);
entityItem = metadata.create(metaClassName);
} }
wm.openEditor( wm.openEditor(
windowInfo, windowInfo,
@ -103,11 +145,14 @@ public class MenuCommand {
params params
); );
} }
sw.stop();
} }
private Map<String, Object> loadParams(Element descriptor) { @Override
public String getDescription() {
return String.format("Opening window: \"%s\"", screen);
}
protected Map<String, Object> loadParams(Element descriptor) {
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
for (Element element : Dom4j.elements(descriptor, "param")) { for (Element element : Dom4j.elements(descriptor, "param")) {
String value = element.attributeValue("value"); String value = element.attributeValue("value");
@ -129,18 +174,81 @@ public class MenuCommand {
} }
} }
String caption = MenuConfig.getMenuItemCaption(item.getId()); String caption = AppBeans.get(MenuConfig.class).getItemCaption(screen);
WindowParams.CAPTION.set(params, caption); WindowParams.CAPTION.set(params, caption);
return params; return params;
} }
private Entity loadEntityInstance(EntityLoadInfo info) { protected Entity loadEntityInstance(EntityLoadInfo info) {
DataService ds = AppBeans.get(DataService.NAME);
LoadContext ctx = new LoadContext(info.getMetaClass()).setId(info.getId()); LoadContext ctx = new LoadContext(info.getMetaClass()).setId(info.getId());
if (info.getViewName() != null) if (info.getViewName() != null) {
ctx.setView(info.getViewName()); ctx.setView(info.getViewName());
Entity entity = ds.load(ctx); }
return entity;
//noinspection unchecked
return AppBeans.get(DataService.class).load(ctx);
}
}
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);
}
} }
} }

View File

@ -65,8 +65,8 @@ public class MenuConfig {
/** /**
* Localized menu item caption. * Localized menu item caption.
* @param id screen ID as defined in <code>screens.xml</code>
* *
* @param id screen ID as defined in <code>screens.xml</code>
* @deprecated Use {@link MenuConfig#getItemCaption(String)} * @deprecated Use {@link MenuConfig#getItemCaption(String)}
*/ */
@Deprecated @Deprecated
@ -180,14 +180,10 @@ public class MenuConfig {
loadDescription(element, menuItem); loadDescription(element, menuItem);
loadMenuItems(element, menuItem); loadMenuItems(element, menuItem);
} else if ("item".equals(element.getName())) { } else if ("item".equals(element.getName())) {
String id = element.attributeValue("id"); menuItem = createMenuItem(element, currentParentItem);
if (!StringUtils.isBlank(id)) {
menuItem = new MenuItem(currentParentItem, id); if (menuItem == null) {
menuItem.setDescriptor(element); continue;
loadIcon(element, menuItem);
loadShortcut(menuItem, element);
loadStylename(element, menuItem);
loadDescription(element, menuItem);
} }
} else if ("separator".equals(element.getName())) { } else if ("separator".equals(element.getName())) {
String id = element.attributeValue("id"); 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) { protected void loadExpanded(Element element, MenuItem menuItem) {
String expanded = element.attributeValue("expanded"); String expanded = element.attributeValue("expanded");
if (StringUtils.isNotEmpty(expanded)) { if (StringUtils.isNotEmpty(expanded)) {

View File

@ -35,6 +35,10 @@ public class MenuItem {
private List<MenuItem> children = new ArrayList<>(); private List<MenuItem> children = new ArrayList<>();
private String id; private String id;
private String screen;
private String runnableClass;
private String bean;
private String beanMethod;
private String stylename; private String stylename;
private String icon; private String icon;
private String description; private String description;
@ -152,4 +156,36 @@ public class MenuItem {
public void setExpanded(boolean expanded) { public void setExpanded(boolean expanded) {
this.expanded = 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;
}
} }

View File

@ -56,6 +56,10 @@
<xs:element type="permissionsType" name="permissions" minOccurs="0" maxOccurs="1"/> <xs:element type="permissionsType" name="permissions" minOccurs="0" maxOccurs="1"/>
</xs:sequence> </xs:sequence>
<xs:attribute type="xs:string" name="id"/> <xs:attribute type="xs:string" name="id"/>
<xs:attribute type="xs:string" name="screen"/>
<xs:attribute type="xs:string" name="class"/>
<xs:attribute type="xs:string" name="bean"/>
<xs:attribute type="xs:string" name="beanMethod"/>
<xs:attribute type="openTypeType" name="openType"/> <xs:attribute type="openTypeType" name="openType"/>
<xs:attribute type="xs:boolean" name="resizable"/> <xs:attribute type="xs:boolean" name="resizable"/>
<xs:attribute type="xs:string" name="shortcut"/> <xs:attribute type="xs:string" name="shortcut"/>

View File

@ -16,13 +16,15 @@
package com.haulmont.cuba.web.sys; 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.ComponentsHelper;
import com.haulmont.cuba.gui.NoSuchScreenException;
import com.haulmont.cuba.gui.components.KeyCombination; import com.haulmont.cuba.gui.components.KeyCombination;
import com.haulmont.cuba.gui.components.Window; import com.haulmont.cuba.gui.components.Window;
import com.haulmont.cuba.gui.components.mainwindow.AppMenu; 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.security.global.UserSession;
import com.haulmont.cuba.web.toolkit.MenuShortcutAction; import com.haulmont.cuba.web.toolkit.MenuShortcutAction;
import com.vaadin.event.ShortcutListener; import com.vaadin.event.ShortcutListener;
@ -58,6 +60,9 @@ public class MenuBuilder {
@Inject @Inject
protected WindowConfig windowConfig; protected WindowConfig windowConfig;
@Inject
protected Scripting scripting;
protected AppMenu appMenu; protected AppMenu appMenu;
public MenuBuilder() { public MenuBuilder() {
@ -172,34 +177,10 @@ public class MenuBuilder {
if (CollectionUtils.isNotEmpty(item.getChildren()) || item.isMenu()) //check item is menu if (CollectionUtils.isNotEmpty(item.getChildren()) || item.isMenu()) //check item is menu
return null; return null;
WindowInfo windowInfo = null; MenuCommand menuCommand = new MenuCommand(item);
if (!item.isSeparator()) {
try {
windowInfo = windowConfig.getWindowInfo(item.getId());
} catch (NoSuchScreenException e) {
log.error("Invalid screen ID for menu item: " + item.getId());
}
}
final MenuCommand command; return menuItem ->
if (windowInfo != null) { menuCommand.execute();
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());
}
}
};
} }
protected boolean isMenuItemEmpty(AppMenu.MenuItem menuItem) { protected boolean isMenuItemEmpty(AppMenu.MenuItem menuItem) {

View File

@ -17,13 +17,14 @@
package com.haulmont.cuba.web.sys; package com.haulmont.cuba.web.sys;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.haulmont.cuba.core.global.DevelopmentException;
import com.haulmont.cuba.gui.ComponentsHelper; 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.KeyCombination;
import com.haulmont.cuba.gui.components.Window; import com.haulmont.cuba.gui.components.Window;
import com.haulmont.cuba.gui.components.mainwindow.SideMenu; 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.haulmont.cuba.security.global.UserSession;
import com.vaadin.event.ShortcutListener; import com.vaadin.event.ShortcutListener;
import com.vaadin.ui.AbstractComponent; import com.vaadin.ui.AbstractComponent;
@ -171,32 +172,10 @@ public class SideMenuBuilder {
if (!item.getChildren().isEmpty() || item.isMenu()) //check item is menu if (!item.getChildren().isEmpty() || item.isMenu()) //check item is menu
return null; return null;
WindowInfo windowInfo = null; MenuCommand command = new MenuCommand(item);
try {
windowInfo = windowConfig.getWindowInfo(item.getId());
} catch (NoSuchScreenException e) {
log.error("Invalid screen ID for menu item: " + item.getId());
}
final MenuCommand command; return event ->
if (windowInfo != null) {
command = new MenuCommand(item, windowInfo);
} else {
command = null;
}
return event -> {
if (command != null) {
command.execute(); 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) { protected boolean isMenuItemEmpty(SideMenu.MenuItem menuItem) {