View Javadoc
1   /**
2    * 
3    */
4   package sf.net.kayestry.components.menu;
5   
6   import java.io.IOException;
7   import java.util.ArrayList;
8   import java.util.Collection;
9   import java.util.HashSet;
10  import java.util.List;
11  import java.util.Set;
12  
13  import org.apache.commons.digester.Digester;
14  import org.springframework.core.io.ClassPathResource;
15  import org.xml.sax.SAXException;
16  
17  import sf.net.kayestry.Globals;
18  import sf.net.kayestry.KayaBaseComponent;
19  import sf.net.kayestry.Visit;
20  
21  import ar.com.jiji.kaya.KayaRuntimeException;
22  import ar.com.jiji.kaya.model.Role;
23  import ar.com.jiji.kaya.model.User;
24  
25  /**
26   * Genera un menu con JavaScript. El menu esta compuesto de items, que pueden
27   * tener items hijos (submenues) o no. Soporta control de acceso en cada item. Y
28   * el menu se lee desde menu.xml, desde el raiz del classpath.<br/> Un menu de
29   * ejemplo para ver la estructura del xml es el siguiente:
30   * 
31   * <pre>
32   *       
33   *          &lt;?xml version=&quot;1.0&quot;?&gt;
34   *          &lt;root&gt;
35   *              &lt;item id=&quot;0&quot; name=&quot;Inicio&quot; pageName=&quot;Home&quot;/&gt;
36   *              &lt;item id=&quot;1&quot; name=&quot;Administracion&quot;&gt;
37   *                  &lt;item name=&quot;Beneficiarios&quot; pageName=&quot;BeneficiarioBrowse&quot;&gt;
38   *                      &lt;allowed-roles&gt;
39   *                          &lt;role&gt;admin&lt;/role&gt;
40   *                      &lt;/allowed-roles&gt;
41   *                  &lt;/item&gt;
42   *              &lt;/item&gt;
43   *              &lt;item id=&quot;2&quot; name=&quot;Reportes&quot;&gt;
44   *                  &lt;allowed-roles&gt;
45   *                      &lt;role&gt;usuario&lt;/role&gt;
46   *                  &lt;/allowed-roles&gt;
47   *              &lt;/item&gt;
48   *              ...
49   *          &lt;/root&gt;
50   *        
51   * </pre>
52   * 
53   * Los roles permitidos son los que existan en la aplicacion, utilizando el
54   * esquema de seguridad que ofrece kommons. Estos roles no se heredan en los
55   * subitems. El id del menu debe ser unico en toda la definicion. Este id es
56   * utilizado para establecer el nombre del elemento <SPAN> que contiene el item.
57   * 
58   * 
59   * @author jmile
60   * @author lparra
61   * 
62   */
63  public abstract class MenuComponent extends KayaBaseComponent {
64  
65  	private static final String DEFAULT_MENU = "menu.xml";
66  
67  	protected abstract String getDefinition();
68  
69  	protected abstract boolean getShowLogin();
70  
71  	private MenuItem currItem;
72  
73  	private MenuItem currSubItem;
74  
75  	public Collection getMenu() {
76  		List<MenuItem> menu = loadMenu();
77  		User usr = getLoggedUser();
78  
79  		List<MenuItem> tmpmenu = filterMenu(menu, usr);
80  
81  		if (getShowLogin()) {
82  			MenuItem auxItem;
83  			if (usr != null)
84  				auxItem = new MenuItem("Logout", Globals.LOGOUT_PAGE);
85  			else
86  				auxItem = new MenuItem("Login", Globals.LOGIN_PAGE);
87  			tmpmenu.add(auxItem);
88  		}
89  
90  		return tmpmenu;
91  	}
92  
93  	private User getLoggedUser() {
94  		Visit visit = getVisit();
95  		User usr = null;
96  
97  		if (visit != null)
98  			usr = visit.getLoggedUser();
99  
100 		return usr;
101 	}
102 
103 	private List<MenuItem> filterMenu(List<MenuItem> menu, User usr) {
104 		// TODO: hacerlo solo una vez
105 		Set<String> roles;
106 		if (usr == null)
107 			roles = new HashSet<String>();
108 		else
109 			roles = getRoleNames(usr.getRoles());
110 		return filterMenu(menu, roles);
111 	}
112 
113 	private List<MenuItem> filterMenu(List<MenuItem> menu, Set<String> roles) {
114 		if (menu == null)
115 			return null;
116 		List<MenuItem> result = new ArrayList<MenuItem>();
117 		for (MenuItem item : menu) {
118 			Set<String> allowed = item.getAllowedRoles();
119 			boolean add = (allowed == null);
120 			if (!add)
121 				for (String aux : roles)
122 					if (allowed.contains(aux)) {
123 						add = true;
124 						break;
125 					}
126 			if (add) {
127 				List<MenuItem> items = item.getSubItems();
128 				List<MenuItem> filtered = filterMenu(items, roles);
129 				item.setSubItems(filtered);
130 				result.add(item);
131 			}
132 		}
133 		return result;
134 	}
135 
136 	private Set<String> getRoleNames(Set<Role> roles) {
137 		Set<String> rolesNames = new HashSet<String>();
138 		for (Role r : roles)
139 			rolesNames.add(r.getName());
140 		return rolesNames;
141 	}
142 
143 	@SuppressWarnings("unchecked")
144 	private List<MenuItem> loadMenu() {
145 		String menuDef = getDefinition();
146 		if (menuDef == null)
147 			menuDef = DEFAULT_MENU;
148 
149 		// TODO: hacer esto una sola vez y no por cada llamado.
150 		Digester digester = new Digester();
151 		digester.setValidating(false);
152 		digester.addObjectCreate("root", ArrayList.class);
153 		digester.addObjectCreate("root/item", MenuItem.class);
154 		digester.addSetProperties("root/item");
155 		digester.addCallMethod("root/item/allowed-roles/role",
156 				"addAllowedRole", 0);
157 		digester.addSetNext("root/item", "add");
158 		digester.addObjectCreate("*/item", MenuItem.class);
159 		digester.addSetProperties("*/item");
160 		digester.addSetNext("*/item", "addSubItem");
161 		digester
162 				.addCallMethod("*/item/allowed-roles/role", "addAllowedRole", 0);
163 		try {
164 			//FIXME: esta es la unica dependencia de spring en kayestry. hay algun problema con los classloaders. si uso getClass().getResource.... no anda cuando corre con la webapp. sacar la dependencia a spring.
165 			return (List<MenuItem>) digester.parse(new ClassPathResource(
166 					menuDef).getInputStream());
167 		} catch (IOException e) {
168 			throw new KayaRuntimeException(e);
169 		} catch (SAXException e) {
170 			throw new KayaRuntimeException(e);
171 		}
172 	}
173 
174 	public void setCurrentMenuItem(MenuItem value) {
175 		currItem = value;
176 	}
177 
178 	public void setCurrentMenuSubItem(MenuItem value) {
179 		currSubItem = value;
180 	}
181 
182 	public MenuItem getMenuSubItem() {
183 		return currSubItem;
184 	}
185 
186 	public MenuItem getMenuItem() {
187 		return currItem;
188 	}
189 }