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}