-
FEATURED COMPONENTS
First time here? Check out the FAQ!
hi all,
i have a technical question about best practice and performance for creating a menu tree.
The menu tree(s) in the planned application should as follows:
1. the menu has follow structure ( + = collapsable ):
  +MainData
     -Tax
     -InvoiceConditions
     -Banks
     - ...
  +OfficeData
     -Customers
     -Orders
     -Employees
     -...
  +Statistical
     -...
  +Setup
     -Usermanagement
     -...
2. is changed by the role of the logged in user
3. should be multi-languagable
---------------------------------------------------------------------------------
A. Is it better to take all existing menu-items in the zul-file
    and by rendering zk looks for each items at the 'if=' attribut?
B. or is it better to build the menu in the underlaying java-file?
----------------------------------------------------------------------------------
C. By my first look it seems that it's a big work to create the tree-menu per coding
    as follows with about 50 items
     // create the menu trees
     Tree menuKategoryFilialstammdaten = new Tree();
     Treechildren treeChildFilialStammdaten = new Treechildren();
     Treeitem treeItem = new Treeitem();
     Treerow treeRow = new Treerow();
     Treecell treeCell = new Treecell();
     treeCell.setLabel("Filialstammdaten");
     treeRow.appendChild(treeCell);
     treeItem.appendChild(treeRow);
     treeChildFilialStammdaten.appendChild(treeItem);
     menuKategoryFilialstammdaten.appendChild(treeChildFilialStammdaten);
     ...
Are there helper classes or is this the false way for creating a tree menu (or using TreeModel? ) ??
Any experience are welcome.
Many thanks for your answers and helps.
Stephan
Stephan,
Actually, I am also trying to do the similar implementation using Menu (your are trying with Tree), you are little head of me :-). When user login, build menu (drill down) depending the organizational unit, roles and group the person belong to. Certainly interested to see how this plays out.
- Meher
Hi,
I am also doing a similar menu from Tree component.
I let my user to maintain(add,update,delete) module, sub-module, and function in my system and then the system will load the Menu from the database. I use SimpleTreeModel and quite a lot HaspMap and TreeMap var(TreeMap is used to maintain the menu item position set by user) to construct all the necessary SimpleTreeNode.
So i think if your system is using "fixed" menu (which mean the menu items are not likely to change), i think using a zul file to render the menu is good enough. Even in production, You can edit the zul directly if needed and no need compiling java etc. Furthermore, zul is more readable, instead of all the messy appendChild or setParent thingy in a java file.
Regards,
John
Here is my opinion.
The real answer depends on your point of view.
If your issue is performance, than a JAVA file would be surely better (zscripts are interpreted by beanshell).
Using Java, I would use ZUL to describe the view, a TreeModel for the data, a renderer to create the tree row and a composer to interact with the zul components.
If your issue is 'how much readable is your code', than ZUL is should be your choice.
About the point C:
If you want to build the tree by java code, don't build it manually. Use a model and e renderer: it will save you lot of code.
Conclusions
Usually I prefer to use the first solution: ZUL + MODEL + RENDERER + COMPOSER.
It makes the code quite readable and easy to write, making the development of the application very fast.
Regards,
Massimiliano
Hello,
My personal favorite for this problem is the following:
- create your own model, for example a simple arraylist with a Java Bean's with properties like: "label", "action", "roles" etc.
- use XStream to write this model to an xml file.
- Make a helper class to generate (dynamically) a tree menu based on the model from above.
The advantages:
- Creating a new menu is now just a matter of copy/paste/edit a new (Xstream) menu xml file.
- And this menu can be role based
gr
@all
many thanks for your proposals. Yes, it's what i thinking too.
I would prefer the following way:
- a empty zul-file (only the window tag)
- a model for the menu entries for filtering the user,role,menu, action
- a renderer
- a controller
At time we have on our Zkoss sample app (Zkoss+Spring+Hibernate) finish the migrating from 'fat' zul-file menu to manually java-generated menu. But the next step should to do it with a model and a renderer because the role,user,menu,action,destination should be come from the database.
I think so it's the best way to reuse this menu logic in other apps or extend by needing.
1. Does somebody have samples for a model and renderer for using as menu logic in
a menu first with two depth (Menu-Category + Menu-Entry ?
2. Our sample app should go for everyone. So we need an address to host the eclipse project.
thanks Stephan
sge(at)forsthaus(dot)de
Hello here is an example how to dynamically create a tree menu based on a dedicated model:
###--- TestMenu.zul -------
<?page id="testZul" title=" New ZUL Title" cacheable="false"
language="xul/html" zscriptLanguage="Java" contentType="text/html;charset=UTF-8"?>
<zk>
<window id="w" use="com.dll.zk.comp.RoleBasedTreeMenuCtrl">
<tree id="idTree" visible="true" height="100px" width="300px"/>
</window>
</zk>
###--- menu.xml
<list>
<com.dll.zk.comp.RoleBasedMenuItem>
<label>Registreer</label>
<description>Registreer de inkomende documenten</description>
<action>REGISTER_INCOMING_DOC</action>
</com.dll.zk.comp.RoleBasedMenuItem>
<com.dll.zk.comp.RoleBasedMenuItem>
<label>Call Me</label>
<description>Bel die klant nu eens op</description>
<roles>
<string>MO_AGENT</string>
</roles>
<action>CUSTOMER_CALLME</action>
<arguments>
<string>P1</string>
</arguments>
</com.dll.zk.comp.RoleBasedMenuItem>
</list>
###--- com/dll/zk/comp/RoleBasedMenuItem.java
package com.dll.zk.comp;
import java.util.ArrayList;
import java.util.List;
public class RoleBasedMenuItem {
private String label;
private String description;
private List<String> roles;
private String action;
private List<String> arguments;
private List<RoleBasedMenuItem> children;
//---- getters & setters omiited
}
###--- com/dll/zk/comp/RoleBasedTreeMenuCtrl.java
package com.dll.zk.comp;
import java.util.List;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zul.Tree;
import org.zkoss.zul.Treecell;
import org.zkoss.zul.Treechildren;
import org.zkoss.zul.Treeitem;
import org.zkoss.zul.Treerow;
import org.zkoss.zul.Window;
import com.dll.cf.crm.constants.EventsConst;
import com.dll.cf.crm.ctrl.CommandCtrl;
import com.dll.cxp.frw.util.XstreamHelper;
import com.dll.zk.helper.EventBroker;
public class RoleBasedTreeMenuCtrl extends Window {
private static final long serialVersionUID = -1636111928349775166L;
private RoleBasedMenuItem menuItems;
public static final String ATTR_MODEL_ITEM = "modelItem";
public void onCreate()
{
testMe();
}
private void testMe() {
CommandCtrl.getInstance();
String roles[] = new String[] {"MO_AGENT"};
generateMenu("/menu.xml", roles);
}
@SuppressWarnings("unchecked")
public void generateMenu(String aResourceName, String aRoles[])
{
List<RoleBasedMenuItem> menuitems = (List<RoleBasedMenuItem>)
XstreamHelper.readObject(this, aResourceName);
generateMenu(menuitems, aRoles);
}
public void generateMenu(List<RoleBasedMenuItem> aMenuItems, String aRoles[])
{
Treechildren treeChildren = getTree().getTreechildren();
if (treeChildren == null) {
treeChildren = new Treechildren();
getTree().appendChild(treeChildren);
}
for (RoleBasedMenuItem rbMenuItem : aMenuItems) {
if (isInRole(rbMenuItem, aRoles)) {
Treerow treerow = new Treerow();
Treecell treecell = makeTreeCell(rbMenuItem);
treerow.appendChild(treecell);
Treeitem treeitem = new Treeitem();
treeitem.appendChild(treerow);
treeChildren.appendChild(treeitem);
}
}
}
@SuppressWarnings("unchecked")
private Treecell makeTreeCell(RoleBasedMenuItem rbMenuItem)
{
Treecell result = new Treecell(rbMenuItem.getLabel());
result.addEventListener("onClick", new HandleMenuClick());
result.getAttributes().put(ATTR_MODEL_ITEM, rbMenuItem);
result.setTooltip(rbMenuItem.getDescription());
return result;
}
/**
* if no menuItem has no roles, then we assume it is for the whole world.
* the same is true if the user has NULL roles.
* @param aRbMenuItem
* @param allowedRoles
* @return
*/
private boolean isInRole(RoleBasedMenuItem aRbMenuItem, String allowedRoles[])
{
if (aRbMenuItem.getRoles() == null || aRbMenuItem.getRoles().size() == 0) {
return true;
} else {
if (allowedRoles == null) {
return true;
} else {
for (String allowedRole : allowedRoles) {
for (String role : aRbMenuItem.getRoles()) {
if (role != null && allowedRole != null &&
allowedRole.toUpperCase().equals(role.toUpperCase())) {
return true;
}
}
}
}
}
return false;
}
public Tree getTree()
{
return (Tree) getFellow("idTree");
}
public RoleBasedMenuItem getMenuItems() {
return menuItems;
}
public void setMenuItems(RoleBasedMenuItem menuItems) {
this.menuItems = menuItems;
}
//--- inner class
private class HandleMenuClick implements EventListener
{
public void onEvent(Event event) throws Exception
{
Treecell tc = (Treecell) event.getTarget();
RoleBasedMenuItem rbItem = (RoleBasedMenuItem) tc.getAttribute(ATTR_MODEL_ITEM);
EventBroker.getInstance().dispatchEvent(EventsConst.MENU_CLICK, rbItem);
}
}
}
###----- com/dll/zk/helper/EventBroker
package com.dll.zk.helper;
import org.zkoss.zk.ui.AbstractComponent;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
public class EventBroker extends AbstractComponent {
private static final long serialVersionUID = 1840221861723658854L;
private static final String KEY = "EventBrokerKey";
public static EventBroker getInstance()
{
EventBroker result = (EventBroker) Executions.getCurrent().getDesktop().getSession().getAttribute(KEY);
if (result == null) {
result = new EventBroker();
Executions.getCurrent().getDesktop().getSession().setAttribute(KEY, result);
}
return result;
}
public void dispatchEvent(String aEventname, Object aData)
{
Events.sendEvent(new Event(aEventname, this, aData));
}
}
@robinbak
Many thanks. It looks interesting. I need a little bit time to check how it's work and do an adaption from the menu.xml to database tables
Stephan
Hi robinbak,
Am facing some problem with the code you post.
Where to find these 3 classes ?
import com.dll.cf.crm.constants.EventsConst;
import com.dll.cf.crm.ctrl.CommandCtrl;
import com.dll.cxp.frw.util.XstreamHelper;
Tks in advance.
Regards
/Slave
Hi Robinbak,
Please I need
import com.dll.cf.crm.constants.EventsConst;
import com.dll.cf.crm.ctrl.CommandCtrl;
import com.dll.cxp.frw.util.XstreamHelper;
to practice this example? can i download sample code from you robinbak?
Fellows,
Just an example!!!!!
My project is using JBOSS 7 + zk (of course) + EJB3.1+PostgreSQL
This is my menu entity. As shown, this entity is bound in task entity.
@Entity @Table( name = "menu" ) @NamedQueries( { @NamedQuery( name = Menu.getTopMenu, query = "select o from Menu o where o.parent is null" ) } ) public class Menu implements Serializable, TaskRelationInterface, SelfRelationInterface<Menu>, Comparable<Menu> { private static final long serialVersionUID = 1L; public static final String getTopMenu = "Menu.getTopMenu"; @Id @Column( name = "mnu_id_in", unique = true, nullable = false ) private Integer id; @ManyToOne @JoinColumn( name = "med_id_in" ) private Media media; @Column( name = "mnu_autocheck_bt" ) private Boolean autocheck; @Column( name = "mnu_checked_bt" ) private Boolean checked; @Column( name = "mnu_checkmark_bt" ) private Boolean checkmark; @Column( name = "mnu_description_ch", nullable = false, length = 64 ) private String description; @Column( name = "mnu_disabled_bt" ) private Boolean disabled; @Column( name = "mnu_separator_before_bt" ) private Boolean separatorBefore; @Column( name = "mnu_sequence_in", nullable = false ) private Integer sequence; @Column( name = "mnu_url_ch", length = 1024 ) private String url; @Column( name = "mnu_image_path_ch", length = 256, nullable = true ) private String imagePath; // bi-directional many-to-one association to Menu @ManyToOne( fetch = FetchType.EAGER ) @JoinColumn( name = "mnu_parent_id" ) private Menu parent; // bi-directional many-to-one association to Menu @OneToMany( mappedBy = "parent", fetch = FetchType.EAGER ) private List<Menu> childs; @ManyToMany( mappedBy = "menus" ) List<Task> tasks; public Menu( ) { } public Integer getId( ) { return this.id; } public void setId( Integer mnuIdIn ) { this.id = mnuIdIn; } public Boolean getAutocheck( ) { return this.autocheck; } public void setAutocheck( Boolean mnuAutocheckBt ) { this.autocheck = mnuAutocheckBt; } public Boolean getChecked( ) { return this.checked; } public void setChecked( Boolean mnuCheckedBt ) { this.checked = mnuCheckedBt; } public Boolean getCheckmark( ) { return this.checkmark; } public void setCheckmark( Boolean mnuCheckmarkBt ) { this.checkmark = mnuCheckmarkBt; } public String getDescription( ) { return this.description; } public void setDescription( String mnuDescriptionCh ) { this.description = mnuDescriptionCh; } public Boolean getDisabled( ) { return this.disabled; } public void setDisabled( Boolean mnuDisabledBt ) { this.disabled = mnuDisabledBt; } public Boolean getSeparatorBefore( ) { return this.separatorBefore; } public void setSeparatorBefore( Boolean mnuSeparatorBeforeBt ) { this.separatorBefore = mnuSeparatorBeforeBt; } public Integer getSequence( ) { return this.sequence; } public void setSequence( Integer mnuSequenceIn ) { this.sequence = mnuSequenceIn; } public String getUrl( ) { return this.url; } public void setUrl( String mnuUrlCh ) { this.url = mnuUrlCh; } public Menu getParent( ) { return this.parent; } @Override public void setParent( Menu parent ) { if ( parent != null ) { if ( parent.equals( getParent( ) ) == false ) { if ( parent.getParent( ) != null ) { parent.getParent( ).remove( this ); } this.parent = parent; getParent( ).add( this ); } } else { this.parent = null; } } public List<Menu> getChilds( ) { if ( this.childs == null ) { this.childs = new ArrayList<Menu>( ); } return this.childs; } public void setChilds( List<Menu> menus ) { this.childs = menus; } public void setMedia( Media media ) { this.media = media; } public Media getMedia( ) { return this.media; } @Override public List<Task> getTasks( ) { return this.tasks; } @Override public void setTasks( List<Task> tasks ) { this.tasks = tasks; } @Override public int compareTo( Menu o ) { if ( o == null ) { return 1; } return getId( ).compareTo( o.getId( ) ); } @Override public boolean equals( Object obj ) { if ( obj instanceof Menu ) { return getId( ).equals( ( (Menu) obj ).getId( ) ); } else if ( obj instanceof Integer ) { return getId( ).equals( obj ); } else { return false; } } @Override public Task add( Task task ) { if ( task != null && getTasks( ).contains( task ) == false ) { getTasks( ).add( task ); task.add( this ); } return task; } @Override public Task remove( Task task ) { if ( task != null ) { int nIndex = getTasks( ).indexOf( task ); if ( nIndex >= 0 ) { getTasks( ).remove( nIndex ); task.remove( this ); } } return task; } @Override public void add( Menu child ) { if ( child != null && getChilds( ).contains( child ) == false ) { assert ( getChilds( ).add( child ) ); child.setParent( this ); } } @Override public Menu remove( Menu child ) { if ( child != null && getChilds( ).contains( child ) ) { assert ( getChilds( ).remove( child ) ); child.setParent( null ); } return child; } @Override public String toString( ) { return getId( ).toString( ) + " - " + getDescription( ); } public String getImagePath( ) { return this.imagePath; } public void setImagePath( String imagePath ) { this.imagePath = imagePath; } }
This is my task Entity. This entity is kind of middle between a menu item and a role.
@Entity @Table( name = "task" ) @NamedQueries( { @NamedQuery( name = Task.getTop, query = "select o from Task o where o.parent is null" ) } ) public class Task implements Serializable, Comparable<Task> { private static final long serialVersionUID = 1L; public static final String getTop = "Task.getTopTasks"; @Id @Column( name = "tsk_id_in", unique = true, nullable = false ) private Integer id; @Column( name = "tsk_description_ch", nullable = false, length = 64 ) private String description; // bi-directional many-to-one association to Role @ManyToOne( fetch = FetchType.EAGER ) @JoinColumn( name = "tsk_parent_id" ) private Task parent; // bi-directional many-to-one association to Role @OneToMany( mappedBy = "parent", fetch = FetchType.EAGER ) private List<Task> childs; @ManyToMany @JoinTable( name = "task_menu", joinColumns = { @JoinColumn( name = "tsk_id_in", nullable = false ) }, inverseJoinColumns = { @JoinColumn( name = "mnu_id_in", nullable = false ) } ) private List<Menu> menus; @ManyToMany( mappedBy = "tasks" ) List<Role> roles; public Task( ) { } public Integer getId( ) { return this.id; } public void setId( Integer tskIdIn ) { this.id = tskIdIn; } public String getDescription( ) { return this.description; } public void setDescription( String tskDescriptionCh ) { this.description = tskDescriptionCh; } public List<Menu> getMenus( ) { if ( this.menus == null ) { this.menus = new ArrayList<Menu>( ); } return this.menus; } public void setMenus( List<Menu> menus ) { this.menus = menus; } public Task getParent( ) { return this.parent; } public void setParent( Task parent ) { this.parent = parent; } public List<Task> getChilds( ) { if ( this.childs == null ) { this.childs = new ArrayList<Task>( ); } return this.childs; } public void setChilds( List<Task> childs ) { this.childs = childs; } public Role remove( Role role ) { if ( role != null ) { int nIndex = getRoles( ).indexOf( role ); if ( nIndex >= 0 ) { getRoles( ).remove( nIndex ); role.remove( this ); } } return role; } public Role add( Role role ) { if ( role != null && getRoles( ).contains( role ) == false ) { getRoles( ).add( role ); } return role; } public List<Role> getRoles( ) { if ( this.roles == null ) { this.roles = new ArrayList<Role>( ); } return this.roles; } public void setRoles( List<Role> roles ) { this.roles = roles; } public Menu remove( Menu menu ) { if ( menu != null ) { int nIndex = getMenus( ).indexOf( menu ); if ( nIndex >= 0 ) { getMenus( ).remove( nIndex ); menu.remove( this ); } } return menu; } public Menu add( Menu menu ) { if ( menu != null && getMenus( ).contains( menu ) == false ) { getMenus( ).add( menu ); } return menu; } @Override public int compareTo( Task o ) { return getId( ).compareTo( o.getId( ) ); } @Override public boolean equals( Object obj ) { if ( obj instanceof Task ) { return getId( ).equals( ( (Task) obj ).getId( ) ); } else { return false; } } @Override public String toString( ) { String str = getId( ).toString( ) + " - " + getDescription( ); if ( getParent( ) != null ) { str += " Parent: " + getParent( ).toString( ); } return str; } public Task remove( Task child ) { if ( child != null ) { int nIndex = getChilds( ).indexOf( child ); if ( nIndex >= 0 ) { getChilds( ).remove( nIndex ); child.setParent( null ); } } return child; } public Task add( Task child ) { if ( child != null && getChilds( ).contains( child ) == false ) { getChilds( ).add( child ); child.setParent( this ); } return child; } }
Role entity. Every system user is bound by a role. So it´s easy to see Role->Task->Menu!!!
@Entity @Table( name = "role" ) public class Role implements Serializable, TaskRelationInterface, SelfRelationInterface<Role> { private static final long serialVersionUID = 1L; @Id @Column( name = "rol_id_in", unique = true, nullable = false ) private Integer id; @Column( name = "rol_default_bt" ) private Boolean rolDefaultBt; @Column( name = "rol_description_ch", nullable = false, length = 64 ) private String description; // bi-directional many-to-one association to Role @ManyToOne( fetch = FetchType.EAGER ) @JoinColumn( name = "rol_parent_id" ) private Role parent; // bi-directional many-to-one association to Role @OneToMany( mappedBy = "parent", fetch = FetchType.EAGER ) private List<Role> childs; @ManyToMany @JoinTable( name = "permission_assignment", joinColumns = { @JoinColumn( name = "rol_id_in", nullable = false ) }, inverseJoinColumns = { @JoinColumn( name = "tsk_id_in", nullable = false ) } ) private List<Task> tasks; public Role( ) { } public Role( Integer id, String description ) { setId( id ); setDescription( description ); } public Integer getId( ) { return this.id; } public void setId( Integer rolIdIn ) { this.id = rolIdIn; } public Boolean getRolDefaultBt( ) { return this.rolDefaultBt; } public void setRolDefaultBt( Boolean rolDefaultBt ) { this.rolDefaultBt = rolDefaultBt; } public String getDescription( ) { return this.description; } public void setDescription( String rolDescriptionCh ) { this.description = rolDescriptionCh; } public Role getParent( ) { return this.parent; } /* * A entidade corrente está recebendo a role como parent */ @Override public void setParent( Role parent ) { if ( parent != null ) { if ( parent.equals( getParent( ) ) == false ) { if ( parent.getParent( ) != null ) { parent.getParent( ).remove( this ); } this.parent = parent; getParent( ).add( this ); } } else { this.parent = null; } } @Override public void add( Role child ) { if ( child != null && getChilds( ).contains( child ) == false ) { getChilds( ).add( child ); child.setParent( this ); } } @Override public Role remove( Role child ) { if ( child != null ) { if ( getChilds( ).contains( child ) ) { getChilds( ).remove( child ); child.setParent( null ); } } return child; } public List<Role> getChilds( ) { if ( this.childs == null ) { this.childs = new ArrayList<Role>( ); } return this.childs; } public void setChilds( List<Role> roles ) { this.childs = roles; } @Override public List<Task> getTasks( ) { if ( this.tasks == null ) { this.tasks = new ArrayList<Task>( ); } return this.tasks; } @Override public void setTasks( List<Task> tasks ) { this.tasks = tasks; } @Override public Task add( Task task ) { if ( task != null && getTasks( ).contains( task ) == false ) { getTasks( ).add( task ); task.add( this ); } return task; } @Override public Task remove( Task task ) { if ( task != null ) { int nIndex = getTasks( ).indexOf( task ); if ( nIndex >= 0 ) { getTasks( ).remove( nIndex ); task.remove( this ); } } return task; } }
This is my menu facade. It´s a simple SessionBean
@Stateless( name = "MenuFacade", mappedName = "MenuFacade" ) @LocalBean public class MenuFacadeBean extends SimpleSessionBean<Menu> implements MenuFacade, MenuFacadeLocal { @EJB private CollaboratorSessionLocal collaboratorSession; @EJB TaskSessionLocal taskSession; @Override protected Class<Menu> getEntityClass( ) { return Menu.class; } /*This is where some system user get his/her menus*/ <b > @Override public List<Menu> getMenus( Collaborator collaborator ) throws ApplicationException { List<Menu> availableMenus = Collections.emptyList( ); if ( collaborator == null ) { return Collections.emptyList( ); } collaborator = this.collaboratorSession.merge( collaborator ); availableMenus = new ArrayList<Menu>( ); for ( Role role : collaborator.getRoles( ) ) { addRoleToMenu( role, availableMenus ); } return availableMenus; } @Override public void addRoleToMenu( Role role, List<Menu> availableMenus ) { if ( role != null ) { for ( Role sub : role.getChilds( ) ) { addRoleToMenu( sub, availableMenus ); } for ( Task task : role.getTasks( ) ) { addTaskToMenu( task, availableMenus ); } } } private void addTaskToMenu( Task task, List<Menu> availableMenus ) { if ( task != null ) { for ( Task child : task.getChilds( ) ) { addTaskToMenu( child, availableMenus ); } for ( Menu menu : task.getMenus( ) ) { addMenus( menu, availableMenus ); } } } private void addMenus( Menu item, List<Menu> availableMenus ) { if ( item != null ) { addMenus( item.getParent( ), availableMenus ); if ( availableMenus.contains( item ) == false ) { availableMenus.add( item ); } } } </b> @Override public List<Menu> getTopContextMenu( ) throws ApplicationException { return findByNamedQuery( Menu.getTopMenu ); } @Override public List<Task> getRootTask( ) { return getTaskSession( ).getRootTasks( ); } protected CollaboratorSessionLocal getCollaboratorSession( ) { return this.collaboratorSession; } protected TaskSessionLocal getTaskSession( ) { return this.taskSession; } @Override public List<Task> getTaks( Menu entity ) { List<Task> tasks = new ArrayList<Task>( ); Menu merged = get( entity.getId( ) ); addTask( tasks, merged ); return tasks; } private void addTask( List<Task> tasks, Menu entity ) { for ( Menu item : entity.getChilds( ) ) { addTask( tasks, item ); } for ( Task task : entity.getTasks( ) ) { if ( tasks.contains( task ) == false ) { tasks.add( task ); } } } @Override public void changeParent( Menu entity, Menu newParent ) { Menu targetEntity = get( entity.getId( ) ); Menu targetParent = get( newParent.getId( ) ); Menu oldParent = targetEntity.getParent( ); if ( oldParent != null ) { targetEntity = oldParent.remove( targetEntity ); } targetParent.add( targetEntity ); } @Override public Menu add( Menu item, List<Task> tasks ) { for ( Task task : tasks ) { add( item, task ); } return item; } @Override public Menu add( Menu item, Task task ) { Menu merged = get( item.getId( ) ); if ( merged != null ) { Task taskMerged = this.taskSession.get( task.getId( ) ); merged.add( taskMerged ); } return merged; } @Override public Menu remove( Menu item, Task task ) { Menu merged = get( item.getId( ) ); if ( merged != null ) { Task taskMerged = this.taskSession.get( task.getId( ) ); merged.remove( taskMerged ); } return merged; } }
This is the controller that really use that SessionBean
public class IndexController extends BaseLoggedMDIController implements IClickEvent { private static final long serialVersionUID = 4709477127092943298L; public static final String queueName = "changeCompanyEventQueue"; private transient MenuFacade session = null; @Wire( "#mainMenu" ) private Menubar mainMenu; private DynamicMenu dynamicMenu = null; @Override public void onClick( MouseEvent evt ) { Menuitem menuItem; if ( evt.getTarget( ) instanceof Menuitem ) { menuItem = (Menuitem) evt.getTarget( ); Object obj; obj = menuItem.getAttribute( MenuController.attrMenu ); if ( obj != null ) { /* * TODO: add menu routine */ } else { if ( SysUtils.isEmpty( menuItem.getValue( ) ) == false ) { loadPage( menuItem.getValue( ), true ); } } } } public void onNotify( CompanyEventChange evt ) throws ApplicationException { if ( getMdiApplication( ) != null && getMdiApplication( ).getChildren( ) != null ) { clear( ); } if ( this.mainMenu == null ) { return; } if ( this.mainMenu.getChildren( ) != null ) { this.mainMenu.getChildren( ).clear( ); } <b > List<br.com.mcampos.ejb.security.menu.Menu> menus = getSession( ).getMenus( getCurrentCollaborator( ) ); for ( br.com.mcampos.ejb.security.menu.Menu item : menus ) { getDynamicMenu( ).getParentComponent( item ); } </b> } private void clear( ) { try { /* * initPage = getUserLocator().getInitialPage( getLoggedInUser() ); * if ( !SysUtils.isEmpty( initPage ) ) { gotoPage( initPage, * mdiApplication ); } else */ getMdiApplication( ).getChildren( ).clear( ); } catch ( Exception e ) { showErrorMessage( e.getMessage( ), "Erro" ); } } private MenuFacade getSession( ) { try { if ( this.session == null ) { this.session = (MenuFacade) ServiceLocator.getInstance( ).getRemoteSession( MenuFacade.class ); } } catch ( NamingException e ) { e.printStackTrace( ); } return this.session; } @Override public void doAfterCompose( Window comp ) throws Exception { super.doAfterCompose( comp ); EventQueues.lookup( queueName ).subscribe( new EventListener<Event>( ) { @Override public void onEvent( Event evt ) { if ( evt instanceof CompanyEventChange ) { try { onNotify( (CompanyEventChange) evt ); } catch ( ApplicationException e ) { e.printStackTrace( ); e = null; } } } } ); } protected DynamicMenu getDynamicMenu( ) { if ( this.dynamicMenu == null ) { this.dynamicMenu = new DynamicMenu( this.mainMenu, this ); } return this.dynamicMenu; } }
This is the real deal! How to make (mount) the menu!!!
public final class DynamicMenu implements Serializable { /** * */ private static final long serialVersionUID = -6069397668281548120L; protected static final String attrMenu = "dto"; private IClickEvent clickEvent; private Menubar mainMenu; public DynamicMenu( Menubar mainMenu, IClickEvent evt ) { setMainMenu( mainMenu ); setClickEvent( evt ); } public Component createMenu( br.com.mcampos.ejb.security.menu.Menu item ) { Component ret; if ( SysUtils.isEmpty( item.getUrl( ) ) == false ) { Menuitem menuItem; menuItem = new Menuitem( item.getDescription( ) ); menuItem.setAutocheck( item.getAutocheck( ) ); menuItem.setChecked( item.getChecked( ) ); menuItem.setCheckmark( item.getCheckmark( ) ); menuItem.setDisabled( item.getDisabled( ) ); menuItem.setAttribute( attrMenu, item.getUrl( ) ); menuItem.setValue( item.getUrl( ) ); menuItem.setImage( item.getImagePath( ) ); createListener( menuItem ); ret = menuItem; } else { Menu menuitem = new Menu( item.getDescription( ) ); menuitem.appendChild( new Menupopup( ) ); ret = menuitem; } ret.setAttribute( "index", item.getSequence( ) ); ret.setAttribute( "id", item.getId( ) ); return ret; } private void createListener( Menuitem item ) { if ( getClickEvent( ) != null ) { item.addEventListener( Events.ON_CLICK, new EventListener<Event>( ) { @Override public void onEvent( Event event ) throws Exception { getClickEvent( ).onClick( (MouseEvent) event ); } } ); } } protected IClickEvent getClickEvent( ) { return this.clickEvent; } protected void setClickEvent( IClickEvent clickEvent ) { this.clickEvent = clickEvent; } protected Menubar getMainMenu( ) { return this.mainMenu; } protected void setMainMenu( Menubar mainMenu ) { this.mainMenu = mainMenu; } public Component getParentComponent( br.com.mcampos.ejb.security.menu.Menu item ) { Component base = null; Component target = null; /* * Recursivamente vai ate o menu pai */ if ( item.getParent( ) != null ) { base = getParentComponent( item.getParent( ) ); if ( base instanceof Menu ) { base = base.getChildren( ).get( 0 ); } } if ( base == null ) { base = getMainMenu( ); } /* * Procura por um menu ja criado com o mesmo id do item */ for ( Component c : base.getChildren( ) ) { Integer id = (Integer) c.getAttribute( "id" ); if ( id != null && id.equals( item.getId( ) ) ) { target = c; break; } } /* * Nao achou entao cria no lugar correto */ if ( target == null ) { for ( int nIndex = 0; nIndex < base.getChildren( ).size( ); nIndex++ ) { Component current = base.getChildren( ).get( nIndex ); Integer sequence = (Integer) current.getAttribute( "index" ); if ( sequence != null && sequence.compareTo( item.getSequence( ) ) > 0 ) { target = createMenu( item ); base.getChildren( ).add( nIndex, target ); break; } } if ( target == null ) { target = createMenu( item ); if ( ( base instanceof Menubar && target instanceof Menu ) || ( base instanceof Menupopup && ( target instanceof Menu || target instanceof Menuitem ) ) ) { base.getChildren( ).add( target ); } } } return target; } }
Some SQL (Postgres) for fun - I really didin't test to see if its working, becouse this is a part of my projetct - as same as above!
DROP TABLE IF EXISTS public.permission_assignment CASCADE ; DROP TABLE IF EXISTS public.role CASCADE ; DROP TABLE IF EXISTS public.task_menu CASCADE ; DROP TABLE IF EXISTS public.task CASCADE ; DROP TABLE IF EXISTS public.menu CASCADE ; CREATE TABLE public.menu ( mnu_id_in INTEGER NOT NULL , mnu_parent_id INTEGER NULL , mnu_description_ch VARCHAR(64) NOT NULL , mnu_url_ch VARCHAR(1024) NULL , mnu_sequence_in INTEGER NOT NULL , med_id_in INTEGER NULL , mnu_separator_before_bt BOOLEAN DEFAULT FALSE NULL , mnu_autocheck_bt BOOLEAN DEFAULT FALSE NULL , mnu_checked_bt BOOLEAN DEFAULT FALSE NULL , mnu_checkmark_bt BOOLEAN DEFAULT FALSE NULL , mnu_disabled_bt BOOLEAN DEFAULT FALSE NULL , mnu_image_path_ch VARCHAR(256) NULL , CONSTRAINT PK_Menu PRIMARY KEY (mnu_id_in) USING INDEX TABLESPACE TB_CLOUD_INDEX ,CONSTRAINT UK_MenuSequence UNIQUE (mnu_parent_id,mnu_sequence_in) USING INDEX TABLESPACE TB_CLOUD_INDEX , CONSTRAINT FK_Menu_ParentMenu FOREIGN KEY (mnu_parent_id) REFERENCES public.menu (mnu_id_in) ON DELETE CASCADE, CONSTRAINT FK_Menu_Image FOREIGN KEY (med_id_in) REFERENCES public.media (med_id_in) ON DELETE SET NULL ) TABLESPACE tb_cloud_data; CREATE UNIQUE INDEX public.UK_MenuSequence ON public.menu (mnu_parent_id ASC,mnu_sequence_in ASC) TABLESPACE tb_cloud_index; DROP TRIGGER IF EXISTS trgDeleteMediaFrommenu ON menu; CREATE TRIGGER trgDeleteMediaFrommenu after delete ON menu FOR EACH ROW EXECUTE PROCEDURE deleteMedia(); CREATE TABLE public.task ( tsk_id_in INTEGER NOT NULL , tsk_parent_id INTEGER NULL , tsk_description_ch VARCHAR(64) NOT NULL , CONSTRAINT XPKTasks PRIMARY KEY (tsk_id_in) USING INDEX TABLESPACE TB_CLOUD_INDEX , CONSTRAINT Fk_task_parent FOREIGN KEY (tsk_parent_id) REFERENCES public.task (tsk_id_in) ON DELETE SET NULL ) TABLESPACE tb_cloud_data; CREATE TABLE public.task_menu ( mnu_id_in INTEGER NOT NULL , tsk_id_in INTEGER NOT NULL , CONSTRAINT XPKTasks_Menus PRIMARY KEY (mnu_id_in,tsk_id_in) USING INDEX TABLESPACE TB_CLOUD_INDEX , CONSTRAINT FK_MenuTask FOREIGN KEY (mnu_id_in) REFERENCES public.menu (mnu_id_in) ON DELETE CASCADE, CONSTRAINT FK_TaskMenu FOREIGN KEY (tsk_id_in) REFERENCES public.task (tsk_id_in) ON DELETE CASCADE ); CREATE TABLE public.role ( rol_id_in INTEGER NOT NULL , rol_parent_id INTEGER NULL , rol_description_ch VARCHAR(64) NOT NULL , rol_default_bt BOOLEAN DEFAULT FALSE NULL , CONSTRAINT XPKRoles PRIMARY KEY (rol_id_in) USING INDEX TABLESPACE TB_CLOUD_INDEX , CONSTRAINT FK_Role_ParentRole FOREIGN KEY (rol_parent_id) REFERENCES public.role (rol_id_in) ON DELETE SET NULL ) TABLESPACE tb_cloud_data;
Hope that helps a little!!!!
TYA
Asked: 2008-11-13 19:14:30 +0800
Seen: 1,815 times
Last updated: Mar 02