001/* 002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and others. 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-2.1.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 * Thomas Roger 016 */ 017 018package org.nuxeo.ecm.platform.usermanager.providers; 019 020import java.io.Serializable; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027 028import org.apache.commons.lang.StringUtils; 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.nuxeo.ecm.core.api.DocumentModel; 032import org.nuxeo.ecm.core.api.DocumentModelList; 033import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl; 034import org.nuxeo.ecm.directory.SizeLimitExceededException; 035import org.nuxeo.ecm.platform.query.api.AbstractPageProvider; 036import org.nuxeo.ecm.platform.usermanager.UserManager; 037import org.nuxeo.runtime.api.Framework; 038 039/** 040 * @since 5.8 041 */ 042public abstract class AbstractUsersPageProvider<T> extends AbstractPageProvider<T> { 043 044 private static final Log log = LogFactory.getLog(UsersPageProvider.class); 045 046 private static final long serialVersionUID = 1L; 047 048 protected static final String USERS_LISTING_MODE_PROPERTY = "usersListingMode"; 049 050 protected static final String ALL_MODE = "all"; 051 052 protected static final String SEARCH_ONLY_MODE = "search_only"; 053 054 protected static final String TABBED_MODE = "tabbed"; 055 056 protected static final String SEARCH_OVERFLOW_ERROR_MESSAGE = "label.security.searchOverFlow"; 057 058 /** 059 * Map with first letter as key and users list as value. 060 */ 061 protected Map<String, DocumentModelList> userCatalog; 062 063 protected List<DocumentModel> pageUsers; 064 065 protected List<DocumentModel> computeCurrentPage() { 066 if (pageUsers == null) { 067 error = null; 068 errorMessage = null; 069 pageUsers = new ArrayList<>(); 070 071 List<DocumentModel> users = new ArrayList<DocumentModel>(); 072 try { 073 UserManager userManager = Framework.getLocalService(UserManager.class); 074 String userListingMode = getUserListingMode(); 075 if (ALL_MODE.equals(userListingMode)) { 076 users = searchAllUsers(userManager); 077 } else if (SEARCH_ONLY_MODE.equals(userListingMode)) { 078 users = searchUsers(userManager); 079 } else if (TABBED_MODE.equals(userListingMode)) { 080 users = searchUsersFromCatalog(userManager); 081 } 082 } catch (SizeLimitExceededException slee) { 083 error = slee; 084 errorMessage = SEARCH_OVERFLOW_ERROR_MESSAGE; 085 log.warn(slee.getMessage(), slee); 086 } 087 088 if (!hasError()) { 089 long resultsCount = users.size(); 090 setResultsCount(resultsCount); 091 // post-filter the results "by hand" to handle pagination 092 long pageSize = getMinMaxPageSize(); 093 if (pageSize == 0) { 094 pageUsers.addAll(users); 095 } else { 096 // handle offset 097 long offset = getCurrentPageOffset(); 098 if (offset <= resultsCount) { 099 for (int i = Long.valueOf(offset).intValue(); i < resultsCount && i < offset + pageSize; i++) { 100 pageUsers.add(users.get(i)); 101 } 102 } 103 } 104 } 105 } 106 return pageUsers; 107 } 108 109 protected String getUserListingMode() { 110 Map<String, Serializable> props = getProperties(); 111 if (props.containsKey(USERS_LISTING_MODE_PROPERTY)) { 112 return (String) props.get(USERS_LISTING_MODE_PROPERTY); 113 } 114 return SEARCH_ONLY_MODE; 115 } 116 117 protected String getFirstParameter() { 118 Object[] parameters = getParameters(); 119 if (parameters.length > 0) { 120 String param = (String) parameters[0]; 121 if (param != null) { 122 return param.trim(); 123 } 124 } 125 return ""; 126 } 127 128 protected List<DocumentModel> searchAllUsers(UserManager userManager) { 129 return userManager.searchUsers(null); 130 } 131 132 protected List<DocumentModel> searchUsers(UserManager userManager) { 133 List<DocumentModel> users = new ArrayList<DocumentModel>(); 134 String searchString = getFirstParameter(); 135 if ("*".equals(searchString)) { 136 users = searchAllUsers(userManager); 137 } else if (!StringUtils.isEmpty(searchString)) { 138 users = userManager.searchUsers(searchString); 139 } 140 return users; 141 } 142 143 protected List<DocumentModel> searchUsersFromCatalog(UserManager userManager) { 144 if (userCatalog == null) { 145 updateUserCatalog(userManager); 146 } 147 String selectedLetter = getFirstParameter(); 148 if (StringUtils.isEmpty(selectedLetter) || !userCatalog.containsKey(selectedLetter)) { 149 Collection<String> catalogLetters = getCatalogLetters(); 150 if (!catalogLetters.isEmpty()) { 151 selectedLetter = catalogLetters.iterator().next(); 152 } 153 } 154 return userCatalog.get(selectedLetter); 155 } 156 157 protected void updateUserCatalog(UserManager userManager) { 158 DocumentModelList allUsers = userManager.searchUsers(null); 159 userCatalog = new HashMap<String, DocumentModelList>(); 160 String userSortField = userManager.getUserSortField(); 161 for (DocumentModel user : allUsers) { 162 // FIXME: this should use a "display name" dedicated API 163 String displayName = null; 164 if (userSortField != null) { 165 // XXX hack, principals have only one model 166 org.nuxeo.ecm.core.api.DataModel dm = user.getDataModels().values().iterator().next(); 167 displayName = (String) dm.getData(userSortField); 168 } 169 if (StringUtils.isEmpty(displayName)) { 170 displayName = user.getId(); 171 } 172 String firstLetter = displayName.substring(0, 1).toUpperCase(); 173 DocumentModelList list = userCatalog.get(firstLetter); 174 if (list == null) { 175 list = new DocumentModelListImpl(); 176 userCatalog.put(firstLetter, list); 177 } 178 list.add(user); 179 } 180 } 181 182 public Collection<String> getCatalogLetters() { 183 if (userCatalog == null) { 184 UserManager userManager = Framework.getService(UserManager.class); 185 updateUserCatalog(userManager); 186 } 187 List<String> list = new ArrayList<String>(userCatalog.keySet()); 188 Collections.sort(list); 189 return list; 190 } 191 192 /** 193 * This page provider does not support sort for now => override what may be contributed in the definition 194 */ 195 @Override 196 public boolean isSortable() { 197 return false; 198 } 199 200 @Override 201 protected void pageChanged() { 202 pageUsers = null; 203 super.pageChanged(); 204 } 205 206 @Override 207 public void refresh() { 208 pageUsers = null; 209 super.refresh(); 210 } 211}