diff --git a/migration/i4.1z/oracle/201710232100_IDEMPIERE-3528.sql b/migration/i4.1z/oracle/201710232100_IDEMPIERE-3528.sql new file mode 100644 index 0000000000..c46d69881a --- /dev/null +++ b/migration/i4.1z/oracle/201710232100_IDEMPIERE-3528.sql @@ -0,0 +1,23 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- IDEMPIERE-3528 Add to Favorite for Menu Search Panel +-- Oct 23, 2017 11:59:39 PM GMT+08:00 +INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','Add To Favourite',0,0,'Y',TO_DATE('2017-10-23 23:59:38','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2017-10-23 23:59:38','YYYY-MM-DD HH24:MI:SS'),100,200435,'AddToFavourite','D','ddbf357f-3f1c-49dd-9ce7-3d8f0cc3b30a') +; + +-- Oct 24, 2017 12:00:05 AM GMT+08:00 +INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','Remove From Favourite',0,0,'Y',TO_DATE('2017-10-24 00:00:05','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2017-10-24 00:00:05','YYYY-MM-DD HH24:MI:SS'),100,200436,'RemoveFromFavourite','D','36985206-17fb-4bf2-a339-3aac5e2601f3') +; + +-- Oct 24, 2017 12:00:51 AM GMT+08:00 +UPDATE AD_Message SET MsgText='Add To Favourites', Value='AddToFavourites',Updated=TO_DATE('2017-10-24 00:00:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Message_ID=200435 +; + +-- Oct 24, 2017 12:00:58 AM GMT+08:00 +UPDATE AD_Message SET MsgText='Remove From Favourites', Value='RemoveFromFavourites',Updated=TO_DATE('2017-10-24 00:00:58','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Message_ID=200436 +; + +SELECT register_migration_script('201710232100_IDEMPIERE-3528.sql') FROM dual +; + diff --git a/migration/i4.1z/postgresql/201710232100_IDEMPIERE-3528.sql b/migration/i4.1z/postgresql/201710232100_IDEMPIERE-3528.sql new file mode 100644 index 0000000000..5f8a5eafbb --- /dev/null +++ b/migration/i4.1z/postgresql/201710232100_IDEMPIERE-3528.sql @@ -0,0 +1,20 @@ +-- IDEMPIERE-3528 Add to Favorite for Menu Search Panel +-- Oct 23, 2017 11:59:39 PM GMT+08:00 +INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','Add To Favourite',0,0,'Y',TO_TIMESTAMP('2017-10-23 23:59:38','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2017-10-23 23:59:38','YYYY-MM-DD HH24:MI:SS'),100,200435,'AddToFavourite','D','ddbf357f-3f1c-49dd-9ce7-3d8f0cc3b30a') +; + +-- Oct 24, 2017 12:00:05 AM GMT+08:00 +INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','Remove From Favourite',0,0,'Y',TO_TIMESTAMP('2017-10-24 00:00:05','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2017-10-24 00:00:05','YYYY-MM-DD HH24:MI:SS'),100,200436,'RemoveFromFavourite','D','36985206-17fb-4bf2-a339-3aac5e2601f3') +; + +-- Oct 24, 2017 12:00:51 AM GMT+08:00 +UPDATE AD_Message SET MsgText='Add To Favourites', Value='AddToFavourites',Updated=TO_TIMESTAMP('2017-10-24 00:00:51','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Message_ID=200435 +; + +-- Oct 24, 2017 12:00:58 AM GMT+08:00 +UPDATE AD_Message SET MsgText='Remove From Favourites', Value='RemoveFromFavourites',Updated=TO_TIMESTAMP('2017-10-24 00:00:58','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Message_ID=200436 +; + +SELECT register_migration_script('201710232100_IDEMPIERE-3528.sql') FROM dual +; + diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java index 8790a1672a..35746720a7 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java @@ -35,6 +35,7 @@ import org.adempiere.webui.component.DrillCommand; import org.adempiere.webui.component.TokenCommand; import org.adempiere.webui.component.ZoomCommand; import org.adempiere.webui.desktop.DefaultDesktop; +import org.adempiere.webui.desktop.FavouriteController; import org.adempiere.webui.desktop.IDesktop; import org.adempiere.webui.session.SessionContextListener; import org.adempiere.webui.session.SessionManager; @@ -233,7 +234,7 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb mSession.saveEx(); } - currSess.setAttribute("Check_AD_User_ID", Env.getAD_User_ID(ctx)); + currSess.setAttribute("Check_AD_User_ID", Env.getAD_User_ID(ctx)); //enable full interface, relook into this when doing preference Env.setContext(ctx, "#ShowTrl", true); @@ -281,6 +282,9 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb Env.setContext(ctx, "#LocalHttpAddr", localHttpAddr.toString()); Clients.response(new AuScript("zAu.cmd0.clearBusy()")); + //init favorite + FavouriteController.getInstance(currSess); + processParameters(); } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/MenuSearchController.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/MenuSearchController.java index f25fd41c11..9f9b4c5933 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/MenuSearchController.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/MenuSearchController.java @@ -20,6 +20,8 @@ import java.util.List; import org.adempiere.webui.component.ListHead; import org.adempiere.webui.component.ListItem; import org.adempiere.webui.component.Listbox; +import org.adempiere.webui.component.ToolBarButton; +import org.adempiere.webui.desktop.FavouriteController; import org.adempiere.webui.theme.ThemeManager; import org.adempiere.webui.util.TreeItemAction; import org.adempiere.webui.util.TreeNodeAction; @@ -47,7 +49,6 @@ import org.zkoss.zul.Listitem; import org.zkoss.zul.ListitemRenderer; import org.zkoss.zul.ListitemRendererExt; import org.zkoss.zul.Textbox; -import org.zkoss.zul.Toolbarbutton; import org.zkoss.zul.Tree; import org.zkoss.zul.Treechildren; import org.zkoss.zul.Treeitem; @@ -61,14 +62,21 @@ import org.zkoss.zul.impl.LabelImageElement; */ public class MenuSearchController implements EventListener{ + public static final String M_TREE_NODE_ATTR = "MTreeNode"; + + private static final String Z_ICON_STAR_O = "z-icon-star-o"; + private static final String Z_ICON_STAR = "z-icon-star"; private static final String ON_SEARCH_ECHO = "onSearchEcho"; private static final String ON_LOAD_MORE = "onLoadMore"; private static final String ONSELECT_TIMESTAMP = "onselect.timestamp"; + private static final String STAR_BUTTON_NAME = "Star"; + private static final String NEW_BUTTON_NAME = "New"; private Tree tree; private Listbox listbox; private ListModelList model; private Vlayout layout; private ListModelList fullModel; + private boolean inStarEvent; private static final String ON_POST_SELECT_TREEITEM_EVENT = "onPostSelectTreeitem"; @@ -196,12 +204,21 @@ public class MenuSearchController implements EventListener{ Listheader listheader = new Listheader(); listhead.appendChild(listheader); listheader = new Listheader(); - ZKUpdateUtil.setWidth(listheader, "32px"); + listheader.setAlign("center"); + ZKUpdateUtil.setWidth(listheader, "28px"); + listhead.appendChild(listheader); + listheader = new Listheader(); + listheader.setAlign("center"); + ZKUpdateUtil.setWidth(listheader, "28px"); listhead.appendChild(listheader); layout.addEventListener(ON_SEARCH_ECHO, this); layout.addEventListener(ON_LOAD_MORE, this); updateListboxModel(model); + + FavouriteController controller = FavouriteController.getInstance(Executions.getCurrent().getSession()); + controller.addDeletedCallback(t -> onDeletedCallback(t)); + controller.addInsertedCallback(t -> onInsertedCallback(t)); } @Override @@ -221,18 +238,34 @@ public class MenuSearchController implements EventListener{ } else if (System.currentTimeMillis() - onSelect.longValue() > 1000) { onSelect(item, Boolean.FALSE); } - } else if (event.getTarget() instanceof Toolbarbutton) { - ListItem item = null; - Component parent = event.getTarget(); - while (parent != null) { - if (parent instanceof ListItem) { - item = (ListItem) parent; - break; + } else if (event.getTarget() instanceof ToolBarButton) { + ToolBarButton btn = (ToolBarButton) event.getTarget(); + if (NEW_BUTTON_NAME.equals(btn.getName())) { + ListItem item = null; + Component parent = event.getTarget(); + while (parent != null) { + if (parent instanceof ListItem) { + item = (ListItem) parent; + break; + } + parent = parent.getParent(); } - parent = parent.getParent(); - } - if (item != null) { - onSelect(item, Boolean.TRUE); + if (item != null) { + onSelect(item, Boolean.TRUE); + } + } else if (STAR_BUTTON_NAME.equals(btn.getName())) { + inStarEvent = true; + FavouriteController controller = FavouriteController.getInstance(Executions.getCurrent().getSession()); + if (Z_ICON_STAR_O.equals(btn.getIconSclass())) { + btn.setIconSclass(Z_ICON_STAR); + btn.setTooltiptext(Msg.getMsg(Env.getCtx(), "RemoveFromFavourites")); + controller.addNode((MTreeNode) btn.getAttribute(M_TREE_NODE_ATTR)); + } else if (Z_ICON_STAR.equals(btn.getIconSclass())) { + btn.setIconSclass(Z_ICON_STAR_O); + btn.setTooltiptext(Msg.getMsg(Env.getCtx(), "AddToFavourites")); + controller.removeNode(((MTreeNode) btn.getAttribute(M_TREE_NODE_ATTR)).getNode_ID()); + } + inStarEvent = false; } } } else if (event.getName().equals(ON_SEARCH_ECHO)) { @@ -241,6 +274,20 @@ public class MenuSearchController implements EventListener{ loadMore(); } } + + private void onInsertedCallback(MTreeNode node) { + if (inStarEvent) return; + ListModel t = listbox.getModel(); + listbox.setModel((ListModel)null); + listbox.setModel(t); + } + + private void onDeletedCallback(Integer nodeId) { + if (inStarEvent) return; + ListModel t = listbox.getModel(); + listbox.setModel((ListModel)null); + listbox.setModel(t); + } private void onSelect(ListItem selected, Boolean newRecord) { MenuItem item = selected.getValue(); @@ -438,6 +485,9 @@ public class MenuSearchController implements EventListener{ } private class MenuItemRenderer implements ListitemRenderer, ListitemRendererExt { + private static final String REMOVE_FROM_FAVOURITES_MSG = "RemoveFromFavourites"; + private static final String ADD_TO_FAVOURITES_MSG = "AddToFavourites"; + @Override public Listitem newListitem(org.zkoss.zul.Listbox listbox) { return new ListItem(); @@ -466,13 +516,41 @@ public class MenuSearchController implements EventListener{ item.appendChild(cell); boolean isWindow = data.getType() != null && data.getType().equals("window"); if (isWindow) { - Toolbarbutton newBtn = new Toolbarbutton(null, ThemeManager.getThemeResource("images/New16.png")); + ToolBarButton newBtn = new ToolBarButton(); + newBtn.setImage(ThemeManager.getThemeResource("images/New16.png")); newBtn.addEventListener(Events.ON_CLICK, MenuSearchController.this); newBtn.setSclass("fav-new-btn"); - newBtn.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "New"))); + newBtn.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), NEW_BUTTON_NAME))); + newBtn.setName(NEW_BUTTON_NAME); cell.appendChild(newBtn); } - } + cell = new Listcell(); + item.appendChild(cell); + MTreeNode node = null; + if (data.getData() instanceof MTreeNode) { + node = (MTreeNode) data.getData(); + } else if (data.getData() instanceof Treeitem) { + Treeitem value = (Treeitem) data.getData(); + if (value != null) { + node = (MTreeNode) value.getAttribute(M_TREE_NODE_ATTR); + } + } + if (node != null) { + FavouriteController controller = FavouriteController.getInstance(Executions.getCurrent().getSession()); + ToolBarButton starBtn = new ToolBarButton(); + starBtn.setAttribute(M_TREE_NODE_ATTR, node); + starBtn.setName(STAR_BUTTON_NAME); + if (controller.hasNode(node.getNode_ID())) { + starBtn.setIconSclass(Z_ICON_STAR); + starBtn.setTooltiptext(Msg.getMsg(Env.getCtx(), REMOVE_FROM_FAVOURITES_MSG)); + } else { + starBtn.setIconSclass(Z_ICON_STAR_O); + starBtn.setTooltiptext(Msg.getMsg(Env.getCtx(), ADD_TO_FAVOURITES_MSG)); + } + cell.appendChild(starBtn); + starBtn.addEventListener(Events.ON_CLICK, MenuSearchController.this); + } + } } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPFavourites.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPFavourites.java index 67115338d1..af41196248 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPFavourites.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPFavourites.java @@ -13,13 +13,13 @@ *****************************************************************************/ package org.adempiere.webui.dashboard; -import static org.compiere.model.SystemIDs.TREE_MENUPRIMARY; - -import java.util.Enumeration; +import java.util.ArrayList; +import java.util.List; import org.adempiere.util.Callback; import org.adempiere.webui.adwindow.ADTabpanel; import org.adempiere.webui.adwindow.ADWindow; +import org.adempiere.webui.desktop.FavouriteController; import org.adempiere.webui.exception.ApplicationException; import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.theme.ITheme; @@ -27,13 +27,12 @@ import org.adempiere.webui.theme.ThemeManager; import org.adempiere.webui.window.FDialog; import org.compiere.model.MMenu; import org.compiere.model.MQuery; -import org.compiere.model.MTree; import org.compiere.model.MTreeNode; -import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Msg; import org.compiere.util.Util; import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.event.DropEvent; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; @@ -74,8 +73,10 @@ public class DPFavourites extends DashboardPanel implements EventListener private Label lblMsg; - private int m_AD_Tree_ID; - + private List links = new ArrayList<>(); + + private boolean inCallingController; + public DPFavourites() { super(); @@ -108,35 +109,44 @@ public class DPFavourites extends DashboardPanel implements EventListener private void createFavouritesPanel() { - int AD_Role_ID = Env.getAD_Role_ID(Env.getCtx()); - int AD_Tree_ID = DB.getSQLValue(null, - "SELECT COALESCE(r.AD_Tree_Menu_ID, ci.AD_Tree_Menu_ID)" - + "FROM AD_ClientInfo ci" - + " INNER JOIN AD_Role r ON (ci.AD_Client_ID=r.AD_Client_ID) " - + "WHERE AD_Role_ID=?", AD_Role_ID); - if (AD_Tree_ID <= 0) - AD_Tree_ID = TREE_MENUPRIMARY; // Menu - - m_AD_Tree_ID = AD_Tree_ID; - - MTree vTree = new MTree(Env.getCtx(), AD_Tree_ID, false, true, null); - MTreeNode m_root = vTree.getRoot(); - Enumeration enTop = m_root.children(); - while(enTop.hasMoreElements()) + FavouriteController controller = FavouriteController.getInstance(Executions.getCurrent().getSession()); + List favorites = controller.getFavourites(); + for (MTreeNode nd : favorites) { - MTreeNode ndTop = (MTreeNode)enTop.nextElement(); - Enumeration en = ndTop.preorderEnumeration(); - while (en.hasMoreElements()) - { - MTreeNode nd = (MTreeNode)en.nextElement(); - if (nd.isOnBar()) { - addNode(nd.getNode_ID(), nd.toString().trim(), nd.getDescription(), getIconFile(nd), (nd.isWindow() && !nd.isForm())); - } - } + addNode(nd); } - + lblMsg = new Label(Msg.getMsg(Env.getCtx(), "DropMenuItemHere")); if(bxFav.getChildren().isEmpty()) bxFav.appendChild(lblMsg); + + controller.addInsertedCallback(t -> onInsertedCallback(t)); + controller.addDeletedCallback(t -> onDeletedCallback(t)); + } + + private void addNode(MTreeNode nd) { + addNode(nd.getNode_ID(), nd.toString().trim(), nd.getDescription(), getIconFile(nd), (nd.isWindow() && !nd.isForm())); + } + + private void onDeletedCallback(Integer nodeId) { + if (inCallingController) return; + for (A link : links) { + String id = (String) link.getAttribute(NODE_ID_ATTR); + if (id.equals(nodeId.toString())) { + removeLinkFromUI(link); + break; + } + } + } + + private void onInsertedCallback(MTreeNode node) { + if (inCallingController) return; + for (A link : links) { + String id = (String) link.getAttribute(NODE_ID_ATTR); + if (id.equals(String.valueOf(node.getNode_ID()))) { + return; + } + } + addNode(node); } protected void addNode(int nodeId, String label, String description, String imageSrc, boolean addNewBtn) { @@ -168,54 +178,9 @@ public class DPFavourites extends DashboardPanel implements EventListener newBtn.setSclass("fav-new-btn"); newBtn.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "New"))); } + links.add(btnFavItem); } - /** - * Make Bar add/remove persistent - * @param add true if add - otherwise remove - * @param Node_ID Node ID - * @return true if updated - */ - private boolean barDBupdate(boolean add, int Node_ID) - { - int AD_Client_ID = Env.getAD_Client_ID(Env.getCtx()); - int AD_Org_ID = Env.getContextAsInt(Env.getCtx(), "#AD_Org_ID"); - int AD_User_ID = Env.getContextAsInt(Env.getCtx(), "#AD_User_ID"); - StringBuilder sql = new StringBuilder(); - if (add) { - // If record already exist, we will only make an update - if (DB.getSQLValueEx(null, "SELECT 1 FROM AD_TreeBar WHERE AD_Tree_ID = ? AND AD_User_ID = ? AND Node_ID = ?", m_AD_Tree_ID, AD_User_ID, Node_ID) == 1) { - sql.append("UPDATE AD_TreeBar SET IsFavourite = 'Y', Updated = Sysdate, UpdatedBy = ").append(AD_User_ID).append(" WHERE AD_Tree_ID = ").append(m_AD_Tree_ID) - .append(" AND AD_User_ID=").append(AD_User_ID) - .append(" AND Node_ID=").append(Node_ID); - } - else // we will create the record - sql.append("INSERT INTO AD_TreeBar " - + "(AD_Tree_ID,AD_User_ID,Node_ID, " - + "AD_Client_ID,AD_Org_ID, " - + "IsActive,Created,CreatedBy,Updated,UpdatedBy)VALUES (") - .append(m_AD_Tree_ID).append(",").append(AD_User_ID).append(",").append(Node_ID).append(",") - .append(AD_Client_ID).append(",").append(AD_Org_ID).append(",") - .append("'Y',SysDate,").append(AD_User_ID).append(",SysDate,").append(AD_User_ID).append(")"); - // if already exist, will result in ORA-00001: unique constraint (ADEMPIERE.AD_TREEBAR_KEY) - } - else { - // if the menu entry is opened at login, we will only remove it from favourites - if (DB.getSQLValueEx(null, "SELECT LoginOpenSeqNo FROM AD_TreeBar WHERE AD_Tree_ID = ? AND AD_User_ID = ? AND Node_ID = ?", m_AD_Tree_ID, AD_User_ID, Node_ID) > 0) { - - sql.append("UPDATE AD_TreeBar SET IsFavourite = 'N', Updated = Sysdate, UpdatedBy = ").append(AD_User_ID).append(" WHERE AD_Tree_ID = ").append(m_AD_Tree_ID) - .append(" AND AD_User_ID=").append(AD_User_ID) - .append(" AND Node_ID=").append(Node_ID); - } - else // otherwise, we remove the record - sql.append("DELETE AD_TreeBar WHERE AD_Tree_ID=").append(m_AD_Tree_ID) - .append(" AND AD_User_ID=").append(AD_User_ID) - .append(" AND Node_ID=").append(Node_ID); - } - int no = DB.executeUpdate(sql.toString(), false, null); - return no == 1; - } - public void onEvent(Event event) { Component comp = event.getTarget(); @@ -318,20 +283,27 @@ public class DPFavourites extends DashboardPanel implements EventListener if(value != null) { int Node_ID = Integer.valueOf(value.toString()); - if(barDBupdate(false, Node_ID)) + FavouriteController controller = FavouriteController.getInstance(Executions.getCurrent().getSession()); + inCallingController = true; + if(controller.removeNode(Node_ID)) { - if (btn.getParent() instanceof Hlayout) - bxFav.removeChild(btn.getParent()); -// bxFav.removeChild(btn); - - if(bxFav.getChildren().isEmpty()) - bxFav.appendChild(lblMsg); - - bxFav.invalidate(); + removeLinkFromUI(btn); } + inCallingController = false; } } + private void removeLinkFromUI(A btn) { + if (btn.getParent() instanceof Hlayout) + bxFav.removeChild(btn.getParent()); + + if(bxFav.getChildren().isEmpty()) + bxFav.appendChild(lblMsg); + + bxFav.invalidate(); + links.remove(btn); + } + /** * Add menu treeitem into the user favorite panel * @param treeitem @@ -341,7 +313,9 @@ public class DPFavourites extends DashboardPanel implements EventListener if(value != null) { int Node_ID = Integer.valueOf(value.toString()); - if(barDBupdate(true, Node_ID)) + FavouriteController controller = FavouriteController.getInstance(Executions.getCurrent().getSession()); + inCallingController = true; + if(controller.addNode(Node_ID)) { String menuType = (String) treeitem.getAttribute("menu.type"); boolean isWindow = menuType != null && menuType.equals("window"); @@ -364,6 +338,7 @@ public class DPFavourites extends DashboardPanel implements EventListener } else { FDialog.error(0, this, "BookmarkExist", null); } + inCallingController = false; } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/FavouriteController.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/FavouriteController.java new file mode 100644 index 0000000000..5d48cb8b25 --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/FavouriteController.java @@ -0,0 +1,187 @@ +/** + * + */ +package org.adempiere.webui.desktop; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.adempiere.util.Callback; +import org.compiere.model.MTree; +import org.compiere.model.MTreeNode; +import org.compiere.util.DB; +import org.compiere.util.Env; +import org.zkoss.zk.ui.Session; + +import static org.compiere.model.SystemIDs.TREE_MENUPRIMARY; + +/** + * @author hengsin + * + */ +public class FavouriteController { + + private static final String DESKTOP_FAVOURITE_CONTROLLER = "desktop.FavouriteController"; + private Map nodeMap; + private int m_AD_Tree_ID; + private MTreeNode rootNode; + private List> deletedCallbacks; + private List> insertedCallbacks; + + private FavouriteController() { + init(); + } + + private void init() { + nodeMap = new LinkedHashMap<>(); + deletedCallbacks = new ArrayList<>(); + insertedCallbacks = new ArrayList<>(); + int AD_Role_ID = Env.getAD_Role_ID(Env.getCtx()); + int AD_Tree_ID = DB.getSQLValue(null, + "SELECT COALESCE(r.AD_Tree_Menu_ID, ci.AD_Tree_Menu_ID)" + + "FROM AD_ClientInfo ci" + + " INNER JOIN AD_Role r ON (ci.AD_Client_ID=r.AD_Client_ID) " + + "WHERE AD_Role_ID=?", AD_Role_ID); + if (AD_Tree_ID <= 0) + AD_Tree_ID = TREE_MENUPRIMARY; // Menu + + m_AD_Tree_ID = AD_Tree_ID; + + MTree vTree = new MTree(Env.getCtx(), AD_Tree_ID, false, true, false, null); + rootNode = vTree.getRoot(); + Enumeration enTop = rootNode.children(); + while(enTop.hasMoreElements()) + { + MTreeNode ndTop = (MTreeNode)enTop.nextElement(); + Enumeration en = ndTop.preorderEnumeration(); + while (en.hasMoreElements()) + { + MTreeNode nd = (MTreeNode)en.nextElement(); + if (nd.isOnBar()) { + nodeMap.put(nd.getNode_ID(), nd); + } + } + } + } + + /** + * Get favourites controller instance for current session + * @param currSess + * @return FavouriteController session instance + */ + public static synchronized FavouriteController getInstance(Session currSess) { + FavouriteController controller = (FavouriteController) currSess.getAttribute(DESKTOP_FAVOURITE_CONTROLLER); + if (controller == null) { + controller = new FavouriteController(); + currSess.setAttribute(DESKTOP_FAVOURITE_CONTROLLER, controller); + } + return controller; + } + + private boolean barDBupdate(boolean add, int Node_ID) + { + int AD_Client_ID = Env.getAD_Client_ID(Env.getCtx()); + int AD_Org_ID = Env.getContextAsInt(Env.getCtx(), "#AD_Org_ID"); + int AD_User_ID = Env.getContextAsInt(Env.getCtx(), "#AD_User_ID"); + StringBuilder sql = new StringBuilder(); + if (add) + sql.append("INSERT INTO AD_TreeBar " + + "(AD_Tree_ID,AD_User_ID,Node_ID, " + + "AD_Client_ID,AD_Org_ID, " + + "IsActive,Created,CreatedBy,Updated,UpdatedBy)VALUES (") + .append(m_AD_Tree_ID).append(",").append(AD_User_ID).append(",").append(Node_ID).append(",") + .append(AD_Client_ID).append(",").append(AD_Org_ID).append(",") + .append("'Y',SysDate,").append(AD_User_ID).append(",SysDate,").append(AD_User_ID).append(")"); + // if already exist, will result in ORA-00001: unique constraint (ADEMPIERE.AD_TREEBAR_KEY) + else + sql.append("DELETE AD_TreeBar WHERE AD_Tree_ID=").append(m_AD_Tree_ID) + .append(" AND AD_User_ID=").append(AD_User_ID) + .append(" AND Node_ID=").append(Node_ID); + int no = DB.executeUpdate(sql.toString(), false, null); + return no == 1; + } + + /** + * Add node (by node id) to favourties + * @param nodeId + * @return true if successfully added + */ + public boolean addNode(int nodeId) { + MTreeNode node = rootNode.findNode(nodeId); + if (node != null) { + return addNode(node); + } + return false; + } + + /** + * add tree node to favourites + * @param node + * @return true if successfully added + */ + public boolean addNode(MTreeNode node) { + if(barDBupdate(true, node.getNode_ID())) { + nodeMap.put(node.getNode_ID(), node); + for (Callback callback : insertedCallbacks) { + callback.onCallback(node); + } + return true; + } + return false; + } + + /** + * remove node (by node id) from favourites + * @param nodeId + * @return true if found and remove + */ + public boolean removeNode(int nodeId) { + if(barDBupdate(false, nodeId)) + { + nodeMap.remove(nodeId); + for (Callback callback : deletedCallbacks) { + callback.onCallback(nodeId); + } + return true; + } + return false; + } + + /** + * @param nodeId + * @return true if node id is in the current favourites list + */ + public boolean hasNode(int nodeId) { + return nodeMap.keySet().contains(nodeId); + } + + /** + * @return List of favourites node + */ + public List getFavourites() { + List list = new ArrayList<>(); + for(int key : nodeMap.keySet()) { + list.add(nodeMap.get(key)); + } + return list; + } + + /** + * add callback for after add node to favourites + * @param callback + */ + public void addInsertedCallback(Callback callback) { + insertedCallbacks.add(callback); + } + + /** + * add callback for after remove node from favourites + * @param callback + */ + public void addDeletedCallback(Callback callback) { + deletedCallbacks.add(callback); + } +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/AbstractMenuPanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/AbstractMenuPanel.java index 073a8cd5e4..78eaa93aec 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/AbstractMenuPanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/AbstractMenuPanel.java @@ -23,6 +23,7 @@ import java.util.Properties; import org.adempiere.util.Callback; import org.adempiere.webui.adwindow.ADTabpanel; import org.adempiere.webui.adwindow.ADWindow; +import org.adempiere.webui.apps.MenuSearchController; import org.adempiere.webui.exception.ApplicationException; import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.theme.ThemeManager; @@ -205,6 +206,7 @@ public abstract class AbstractMenuPanel extends Panel implements EventListener