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