001/*
002 * (C) Copyright 2013 Nuxeo SA (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-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 *     <a href="mailto:grenard@nuxeo.com">Guillaume</a>
016 */
017package org.nuxeo.ecm.platform.ui.select2.automation;
018
019import java.io.Serializable;
020import java.util.ArrayList;
021import java.util.List;
022import java.util.Locale;
023
024import net.sf.json.JSONArray;
025import net.sf.json.JSONObject;
026
027import org.apache.commons.lang.StringUtils;
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030import org.nuxeo.common.utils.i18n.I18NUtils;
031import org.nuxeo.ecm.automation.OperationContext;
032import org.nuxeo.ecm.automation.core.Constants;
033import org.nuxeo.ecm.automation.core.annotations.Context;
034import org.nuxeo.ecm.automation.core.annotations.Operation;
035import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
036import org.nuxeo.ecm.automation.core.annotations.Param;
037import org.nuxeo.ecm.core.api.Blob;
038import org.nuxeo.ecm.core.api.Blobs;
039import org.nuxeo.ecm.core.api.DocumentModel;
040import org.nuxeo.ecm.core.api.DocumentModelList;
041import org.nuxeo.ecm.core.api.NuxeoGroup;
042import org.nuxeo.ecm.core.api.NuxeoPrincipal;
043import org.nuxeo.ecm.core.schema.SchemaManager;
044import org.nuxeo.ecm.core.schema.types.Field;
045import org.nuxeo.ecm.core.schema.types.QName;
046import org.nuxeo.ecm.core.schema.types.Schema;
047import org.nuxeo.ecm.directory.Directory;
048import org.nuxeo.ecm.directory.SizeLimitExceededException;
049import org.nuxeo.ecm.directory.api.DirectoryService;
050import org.nuxeo.ecm.platform.ui.select2.common.Select2Common;
051import org.nuxeo.ecm.platform.usermanager.UserAdapter;
052import org.nuxeo.ecm.platform.usermanager.UserManager;
053
054/**
055 * SuggestUser Operation.
056 *
057 * @since 5.7.3
058 */
059@Operation(id = SuggestUserEntries.ID, category = Constants.CAT_SERVICES, label = "Get user/group suggestion", description = "Get the user/group list of the running instance. This is returning a blob containing a serialized JSON array..", addToStudio = false)
060public class SuggestUserEntries {
061
062    @SuppressWarnings("unused")
063    private static final Log log = LogFactory.getLog(SuggestUserEntries.class);
064
065    public static final String ID = "UserGroup.Suggestion";
066
067    @Context
068    protected OperationContext ctx;
069
070    @Context
071    protected SchemaManager schemaManager;
072
073    @Param(name = "searchTerm", alias = "prefix", required = false)
074    protected String prefix;
075
076    @Param(name = "searchType", required = false)
077    protected String searchType;
078
079    @Param(name = "groupRestriction", required = false, description = "Enter the id of a group to suggest only user from this group.")
080    protected String groupRestriction;
081
082    /**
083     * @since 7.10
084     */
085    @Param(name = "hideAdminGroups", required = false, description = "If set, remove all administrator groups from the suggestions")
086    protected boolean hideAdminGroups;
087
088    @Param(name = "userSuggestionMaxSearchResults", required = false)
089    protected Integer userSuggestionMaxSearchResults;
090
091    @Param(name = "firstLabelField", required = false)
092    protected String firstLabelField;
093
094    @Param(name = "secondLabelField", required = false)
095    protected String secondLabelField;
096
097    @Param(name = "thirdLabelField", required = false)
098    protected String thirdLabelField;
099
100    @Param(name = "hideFirstLabel", required = false)
101    protected boolean hideFirstLabel = false;
102
103    @Param(name = "hideSecondLabel", required = false)
104    protected boolean hideSecondLabel = false;
105
106    @Param(name = "hideThirdLabel", required = false)
107    protected boolean hideThirdLabel;
108
109    @Param(name = "displayEmailInSuggestion", required = false)
110    protected boolean displayEmailInSuggestion;
111
112    @Param(name = "hideIcon", required = false)
113    protected boolean hideIcon;
114
115    @Context
116    protected UserManager userManager;
117
118    @Context
119    protected DirectoryService directoryService;
120
121    @Param(name = "lang", required = false)
122    protected String lang;
123
124    @OperationMethod
125    public Blob run() {
126        JSONArray result = new JSONArray();
127        boolean isGroupRestriction = !StringUtils.isBlank(groupRestriction);
128        boolean groupOnly = false;
129        boolean userOnly = isGroupRestriction;
130
131        if (!isGroupRestriction && searchType != null && !searchType.isEmpty()) {
132            if (searchType.equals(Select2Common.USER_TYPE)) {
133                userOnly = true;
134            } else if (searchType.equals(Select2Common.GROUP_TYPE)) {
135                groupOnly = true;
136            }
137        }
138        try {
139            DocumentModelList userList = null;
140            DocumentModelList groupList = null;
141            if (!groupOnly) {
142                Schema schema = schemaManager.getSchema(userManager.getUserSchemaName());
143                userList = userManager.searchUsers(prefix);
144                Directory userDir = directoryService.getDirectory(userManager.getUserDirectoryName());
145                for (DocumentModel user : userList) {
146                    JSONObject obj = new JSONObject();
147                    for (Field field : schema.getFields()) {
148                        QName fieldName = field.getName();
149                        String key = fieldName.getLocalName();
150                        Serializable value = user.getPropertyValue(fieldName.getPrefixedName());
151                        if (key.equals(userDir.getPasswordField())) {
152                            continue;
153                        }
154                        obj.element(key, value);
155                    }
156                    String userId = user.getId();
157                    obj.put(Select2Common.ID, userId);
158                    obj.put(Select2Common.TYPE_KEY_NAME, Select2Common.USER_TYPE);
159                    obj.put(Select2Common.PREFIXED_ID_KEY_NAME, NuxeoPrincipal.PREFIX + userId);
160                    Select2Common.computeUserLabel(obj, firstLabelField, secondLabelField, thirdLabelField,
161                            hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, userId);
162                    Select2Common.computeUserGroupIcon(obj, hideIcon);
163                    if (isGroupRestriction) {
164                        // We need to load all data about the user particularly
165                        // its
166                        // groups.
167                        user = userManager.getUserModel(userId);
168                        UserAdapter userAdapter = user.getAdapter(UserAdapter.class);
169                        List<String> groups = userAdapter.getGroups();
170                        if (groups != null && groups.contains(groupRestriction)) {
171                            result.add(obj);
172                        }
173                    } else {
174                        result.add(obj);
175                    }
176                }
177            }
178            if (!userOnly) {
179                Schema schema = schemaManager.getSchema(userManager.getGroupSchemaName());
180                groupList = userManager.searchGroups(prefix);
181                List<String> admins = new ArrayList<>();
182                if (hideAdminGroups) {
183                    admins = userManager.getAdministratorsGroups();
184                }
185                groupLoop:
186                for (DocumentModel group : groupList) {
187                    if (hideAdminGroups) {
188                        for (String adminGroupName : admins) {
189                            if (adminGroupName.equals(group.getId())) {
190                                break groupLoop;
191                            }
192                        }
193                    }
194                    JSONObject obj = new JSONObject();
195                    for (Field field : schema.getFields()) {
196                        QName fieldName = field.getName();
197                        String key = fieldName.getLocalName();
198                        Serializable value = group.getPropertyValue(fieldName.getPrefixedName());
199                        obj.element(key, value);
200                    }
201                    String groupId = group.getId();
202                    obj.put(Select2Common.ID, groupId);
203                    // If the group hasn't an label, let's put the groupid
204                    Select2Common.computeGroupLabel(obj, groupId, userManager.getGroupLabelField(), hideFirstLabel);
205                    obj.put(Select2Common.TYPE_KEY_NAME, Select2Common.GROUP_TYPE);
206                    obj.put(Select2Common.PREFIXED_ID_KEY_NAME, NuxeoGroup.PREFIX + groupId);
207                    Select2Common.computeUserGroupIcon(obj, hideIcon);
208                    result.add(obj);
209                }
210            }
211
212            // Limit size results.
213            int userSize = userList != null ? userList.size() : 0;
214            int groupSize = groupList != null ? groupList.size() : 0;
215            int totalSize = userSize + groupSize;
216            if (userSuggestionMaxSearchResults != null && userSuggestionMaxSearchResults > 0) {
217                if (userSize > userSuggestionMaxSearchResults || groupSize > userSuggestionMaxSearchResults
218                        || totalSize > userSuggestionMaxSearchResults) {
219                    throw new SizeLimitExceededException();
220                }
221            }
222
223        } catch (SizeLimitExceededException e) {
224            return searchOverflowMessage();
225        }
226
227        return Blobs.createBlob(result.toString(), "application/json");
228    }
229
230    /**
231     * @return searchOverflowMessage
232     * @since 5.7.3
233     */
234    private Blob searchOverflowMessage() {
235        JSONArray result = new JSONArray();
236        JSONObject obj = new JSONObject();
237        obj.put(Select2Common.LABEL,
238                I18NUtils.getMessageString("messages", "label.security.searchOverFlow", new Object[0], getLocale()));
239        result.add(obj);
240        return Blobs.createBlob(result.toString(), "application/json");
241    }
242
243    protected String getLang() {
244        if (lang == null) {
245            lang = (String) ctx.get("lang");
246            if (lang == null) {
247                lang = Select2Common.DEFAULT_LANG;
248            }
249        }
250        return lang;
251    }
252
253    protected Locale getLocale() {
254        return new Locale(getLang());
255    }
256
257}