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