001/* 002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nuxeo - initial API and implementation 016 * 017 * $Id$ 018 */ 019 020package org.nuxeo.ecm.webapp.security; 021 022import static org.jboss.seam.ScopeType.SESSION; 023 024import java.io.Serializable; 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.HashMap; 028import java.util.List; 029import java.util.Map; 030 031import javax.faces.model.SelectItem; 032 033import org.apache.commons.lang.StringUtils; 034import org.apache.commons.logging.Log; 035import org.apache.commons.logging.LogFactory; 036import org.jboss.seam.annotations.In; 037import org.jboss.seam.annotations.Name; 038import org.jboss.seam.annotations.Scope; 039import org.jboss.seam.contexts.Context; 040import org.jboss.seam.contexts.Contexts; 041import org.nuxeo.ecm.core.api.CoreSession; 042import org.nuxeo.ecm.core.api.DocumentModel; 043import org.nuxeo.ecm.core.api.DocumentModelList; 044import org.nuxeo.ecm.core.api.NuxeoPrincipal; 045import org.nuxeo.ecm.core.api.PropertyException; 046import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl; 047import org.nuxeo.ecm.directory.SizeLimitExceededException; 048import org.nuxeo.ecm.platform.usermanager.UserManager; 049import org.nuxeo.ecm.webapp.helpers.ResourcesAccessor; 050 051/** 052 * POJO class that extracts and holds the list of the users from backend. 053 * 054 * @author Razvan Caraghin 055 * @author <a href="mailto:tmartins@nuxeo.com">Thierry Martins</a> 056 */ 057@Name("principalListManager") 058@Scope(SESSION) 059public class PrincipalListManager implements Serializable { 060 061 public static final String USER_TYPE = "USER_TYPE"; 062 063 public static final String GROUP_TYPE = "GROUP_TYPE"; 064 065 public static final String USER_GROUP_TYPE = "USER_GROUP_TYPE"; 066 067 public static final String USER_ICON = "/icons/user.png"; 068 069 public static final String GROUP_ICON = "/icons/group.png"; 070 071 public static final String USER_ICON_ALT = "user.png"; 072 073 public static final String GROUP_ICON_ALT = "group.png"; 074 075 public static final int MAX_SEARCH_RESULTS = 20; 076 077 private static final long serialVersionUID = 1859670282887307916L; 078 079 private static final Log log = LogFactory.getLog(PrincipalListManager.class); 080 081 public final Map<String, String> iconPath; 082 083 public final Map<String, String> iconAlt; 084 085 @In(create = true, required = false) 086 protected transient CoreSession documentManager; 087 088 @In(create = true) 089 protected transient UserManager userManager; 090 091 @In(create = true) 092 protected transient ResourcesAccessor resourcesAccessor; 093 094 protected SelectItem[] availablePrincipals; 095 096 protected Map<String, String> principalTypes = new HashMap<String, String>(); 097 098 protected String selectedPrincipal; 099 100 protected String searchType; 101 102 protected List<String> selectedUsers; 103 104 @In(required = false) 105 String searchFilter; 106 107 private boolean searchOverflow; 108 109 // cache of previous results 110 protected transient List<Map<String, Object>> previousSuggestedEntries; 111 112 // previous search filter 113 protected transient String previousSearchFilter; 114 115 // previous search type 116 protected transient String previousSearchType; 117 118 // previous search overflow 119 protected transient boolean previousSearchOverflow; 120 121 public PrincipalListManager() { 122 iconPath = new HashMap<String, String>(); 123 iconAlt = new HashMap<String, String>(); 124 iconPath.put(USER_TYPE, USER_ICON); 125 iconPath.put(GROUP_TYPE, GROUP_ICON); 126 127 iconAlt.put(USER_TYPE, USER_ICON_ALT); 128 iconAlt.put(GROUP_TYPE, GROUP_ICON_ALT); 129 searchType = USER_GROUP_TYPE; 130 } 131 132 public String getSearchFilter() { 133 return searchFilter; 134 } 135 136 public void setSearchFilter(String searchFilter) { 137 Context pageContext = Contexts.getPageContext(); 138 if (pageContext != null) { 139 pageContext.set("searchFilter", searchFilter); 140 } 141 this.searchFilter = searchFilter; 142 } 143 144 public String getSelectedPrincipal() { 145 return selectedPrincipal; 146 } 147 148 public String getPrincipalType(String name) { 149 // happens when used in NXMethodResults in A4JCalls !!! 150 if (name == null) { 151 return null; 152 } 153 if (principalTypes == null) { 154 principalTypes = new HashMap<String, String>(); 155 } 156 String type = principalTypes.get(name); 157 if (type == null) { 158 if (userManager.getGroup(name) != null) { 159 type = GROUP_TYPE; 160 } else { 161 type = USER_TYPE; 162 } 163 principalTypes.put(name, type); 164 } 165 return type; 166 } 167 168 public void setSelectedPrincipal(String selectedPrincipal) { 169 this.selectedPrincipal = selectedPrincipal; 170 } 171 172 protected DocumentModelList getSuggestedUsers() { 173 if (searchFilter == null || searchFilter.length() == 0) { 174 return new DocumentModelListImpl(); 175 } 176 177 DocumentModelList result; 178 try { 179 result = userManager.searchUsers(searchFilter); 180 } catch (SizeLimitExceededException e) { 181 searchOverflow = true; 182 return new DocumentModelListImpl(); 183 } 184 185 if (result.size() > MAX_SEARCH_RESULTS) { 186 searchOverflow = true; 187 return new DocumentModelListImpl(); 188 } 189 return result; 190 } 191 192 protected DocumentModelList getSuggestedGroups() { 193 if (searchFilter == null || searchFilter.length() == 0) { 194 return new DocumentModelListImpl(); 195 } 196 197 DocumentModelList result; 198 try { 199 result = userManager.searchGroups(searchFilter); 200 } catch (SizeLimitExceededException e) { 201 searchOverflow = true; 202 return new DocumentModelListImpl(); 203 } 204 205 if (result.size() > MAX_SEARCH_RESULTS) { 206 searchOverflow = true; 207 return new DocumentModelListImpl(); 208 } 209 return result; 210 } 211 212 public List<Map<String, Object>> getSuggestedEntries() { 213 if (searchFilter == null || searchFilter.length() == 0) { 214 return Collections.emptyList(); 215 } 216 if (searchFilter.equals(previousSearchFilter) && searchType.equals(previousSearchType)) { 217 searchOverflow = previousSearchOverflow; 218 return previousSuggestedEntries; 219 } 220 221 searchOverflow = false; 222 223 DocumentModelList users; 224 if (USER_TYPE.equals(searchType) || USER_GROUP_TYPE.equals(searchType) || StringUtils.isEmpty(searchType)) { 225 users = getSuggestedUsers(); 226 } else { 227 users = new DocumentModelListImpl(); 228 } 229 230 DocumentModelList groups; 231 if (GROUP_TYPE.equals(searchType) || USER_GROUP_TYPE.equals(searchType) || StringUtils.isEmpty(searchType)) { 232 groups = getSuggestedGroups(); 233 } else { 234 groups = new DocumentModelListImpl(); 235 } 236 237 List<Map<String, Object>> result = new ArrayList<Map<String, Object>>(users.size() + groups.size()); 238 239 for (DocumentModel user : users) { 240 if (user == null) { 241 continue; 242 } 243 NuxeoPrincipal principal = userManager.getPrincipal(user.getId()); 244 String name = principal.getName(); 245 StringBuilder label = new StringBuilder(name).append(" ("); 246 if (principal.getFirstName() != null) { 247 label.append(principal.getFirstName()); 248 } 249 if (principal.getLastName() != null) { 250 label.append(' ').append(principal.getLastName()); 251 } 252 label.append(')'); 253 254 Map<String, Object> entry = new HashMap<String, Object>(); 255 entry.put("label", label.toString()); 256 entry.put("id", name); 257 entry.put("icon", "icons/user.png"); 258 result.add(entry); 259 } 260 261 for (DocumentModel group : groups) { 262 Map<String, Object> entry = new HashMap<String, Object>(); 263 try { 264 entry.put("label", 265 group.getProperty(userManager.getGroupSchemaName(), userManager.getGroupLabelField())); 266 } catch (PropertyException e) { 267 log.warn("Unable to get group label of " + group.getId()); 268 log.debug(e); 269 entry.put("label", group.getId()); 270 } 271 entry.put("id", group.getId()); 272 entry.put("icon", "icons/group.png"); 273 result.add(entry); 274 } 275 276 // put in cache 277 previousSuggestedEntries = result; 278 previousSearchOverflow = searchOverflow; 279 previousSearchType = searchType; 280 previousSearchFilter = searchFilter; 281 return result; 282 } 283 284 public boolean getDisplaySearchResults() { 285 return searchFilter != null && searchFilter.length() != 0; 286 } 287 288 public void resetSearchFilter() { 289 searchFilter = null; 290 } 291 292 public String addToSelectedUsers(String userName) { 293 if (selectedUsers == null) { 294 selectedUsers = new ArrayList<String>(); 295 } 296 297 if (!selectedUsers.contains(userName)) { 298 selectedUsers.add(userName); 299 } 300 return null; 301 } 302 303 public String removeFromSelectedUsers(String userName) { 304 if (selectedUsers == null) { 305 selectedUsers = new ArrayList<String>(); 306 } 307 308 if (selectedUsers.contains(userName)) { 309 selectedUsers.remove(userName); 310 } 311 return null; 312 } 313 314 public List<String> getSelectedUsers() { 315 if (selectedUsers == null) { 316 return new ArrayList<String>(); 317 } 318 return selectedUsers; 319 } 320 321 public void setSelectedUsers(List<String> selectedUsers) { 322 this.selectedUsers = selectedUsers; 323 } 324 325 public boolean getSelectedUserListEmpty() { 326 return selectedUsers == null || selectedUsers.isEmpty(); 327 } 328 329 public void resetSelectedUserList() { 330 selectedUsers = null; 331 } 332 333 public boolean getSearchOverflow() { 334 return searchOverflow; 335 } 336 337 public String getSearchType() { 338 return searchType; 339 } 340 341 public void setSearchType(String searchType) { 342 if (searchType == null) { 343 searchType = USER_GROUP_TYPE; 344 } 345 this.searchType = searchType; 346 } 347 348}