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}