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