001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.usersadmin.util;
016    
017    import com.liferay.portal.exception.NoSuchContactException;
018    import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery;
019    import com.liferay.portal.kernel.dao.orm.IndexableActionableDynamicQuery;
020    import com.liferay.portal.kernel.exception.PortalException;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.search.BaseIndexer;
024    import com.liferay.portal.kernel.search.BooleanClauseOccur;
025    import com.liferay.portal.kernel.search.BooleanQuery;
026    import com.liferay.portal.kernel.search.Document;
027    import com.liferay.portal.kernel.search.Field;
028    import com.liferay.portal.kernel.search.IndexWriterHelperUtil;
029    import com.liferay.portal.kernel.search.Indexer;
030    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
031    import com.liferay.portal.kernel.search.SearchContext;
032    import com.liferay.portal.kernel.search.Summary;
033    import com.liferay.portal.kernel.search.filter.BooleanFilter;
034    import com.liferay.portal.kernel.search.filter.TermsFilter;
035    import com.liferay.portal.kernel.spring.osgi.OSGiBeanProperties;
036    import com.liferay.portal.kernel.util.ArrayUtil;
037    import com.liferay.portal.kernel.util.GetterUtil;
038    import com.liferay.portal.kernel.util.Validator;
039    import com.liferay.portal.kernel.workflow.WorkflowConstants;
040    import com.liferay.portal.model.Contact;
041    import com.liferay.portal.model.Organization;
042    import com.liferay.portal.model.User;
043    import com.liferay.portal.model.impl.ContactImpl;
044    import com.liferay.portal.security.auth.FullNameGenerator;
045    import com.liferay.portal.security.auth.FullNameGeneratorFactory;
046    import com.liferay.portal.service.OrganizationLocalServiceUtil;
047    import com.liferay.portal.service.UserLocalServiceUtil;
048    
049    import java.util.HashSet;
050    import java.util.LinkedHashMap;
051    import java.util.Locale;
052    import java.util.Map;
053    import java.util.Set;
054    
055    import javax.portlet.PortletRequest;
056    import javax.portlet.PortletResponse;
057    
058    /**
059     * @author Raymond Aug??
060     * @author Zsigmond Rab
061     * @author Hugo Huijser
062     */
063    @OSGiBeanProperties
064    public class UserIndexer extends BaseIndexer<User> {
065    
066            public static final String CLASS_NAME = User.class.getName();
067    
068            public static long getUserId(Document document) {
069                    return GetterUtil.getLong(document.get(Field.USER_ID));
070            }
071    
072            public UserIndexer() {
073                    setDefaultSelectedFieldNames(
074                            Field.ASSET_TAG_NAMES, Field.COMPANY_ID, Field.ENTRY_CLASS_NAME,
075                            Field.ENTRY_CLASS_PK, Field.GROUP_ID, Field.MODIFIED_DATE,
076                            Field.SCOPE_GROUP_ID, Field.UID, Field.USER_ID);
077                    setPermissionAware(true);
078                    setStagingAware(false);
079            }
080    
081            @Override
082            public String getClassName() {
083                    return CLASS_NAME;
084            }
085    
086            @Override
087            public void postProcessContextBooleanFilter(
088                            BooleanFilter contextBooleanFilter, SearchContext searchContext)
089                    throws Exception {
090    
091                    int status = GetterUtil.getInteger(
092                            searchContext.getAttribute(Field.STATUS),
093                            WorkflowConstants.STATUS_APPROVED);
094    
095                    if (status != WorkflowConstants.STATUS_ANY) {
096                            contextBooleanFilter.addRequiredTerm(Field.STATUS, status);
097                    }
098    
099                    LinkedHashMap<String, Object> params =
100                            (LinkedHashMap<String, Object>)searchContext.getAttribute("params");
101    
102                    if (params == null) {
103                            return;
104                    }
105    
106                    for (Map.Entry<String, Object> entry : params.entrySet()) {
107                            String key = entry.getKey();
108                            Object value = entry.getValue();
109    
110                            if (value == null) {
111                                    continue;
112                            }
113    
114                            Class<?> clazz = value.getClass();
115    
116                            if (clazz.isArray()) {
117                                    Object[] values = (Object[])value;
118    
119                                    if (values.length == 0) {
120                                            continue;
121                                    }
122                            }
123    
124                            addContextQueryParams(
125                                    contextBooleanFilter, searchContext, key, value);
126                    }
127            }
128    
129            @Override
130            public void postProcessSearchQuery(
131                            BooleanQuery searchQuery, BooleanFilter fullQueryBooleanFilter,
132                            SearchContext searchContext)
133                    throws Exception {
134    
135                    addSearchTerm(searchQuery, searchContext, "city", false);
136                    addSearchTerm(searchQuery, searchContext, "country", false);
137                    addSearchTerm(searchQuery, searchContext, "emailAddress", false);
138                    addSearchTerm(searchQuery, searchContext, "firstName", false);
139                    addSearchTerm(searchQuery, searchContext, "fullName", false);
140                    addSearchTerm(searchQuery, searchContext, "lastName", false);
141                    addSearchTerm(searchQuery, searchContext, "middleName", false);
142                    addSearchTerm(searchQuery, searchContext, "region", false);
143                    addSearchTerm(searchQuery, searchContext, "screenName", false);
144                    addSearchTerm(searchQuery, searchContext, "street", false);
145                    addSearchTerm(searchQuery, searchContext, "zip", false);
146    
147                    LinkedHashMap<String, Object> params =
148                            (LinkedHashMap<String, Object>)searchContext.getAttribute("params");
149    
150                    if (params != null) {
151                            String expandoAttributes = (String)params.get("expandoAttributes");
152    
153                            if (Validator.isNotNull(expandoAttributes)) {
154                                    addSearchExpando(searchQuery, searchContext, expandoAttributes);
155                            }
156                    }
157            }
158    
159            protected void addContextQueryParams(
160                            BooleanFilter contextFilter, SearchContext searchContext,
161                            String key, Object value)
162                    throws Exception {
163    
164                    if (key.equals("usersGroups")) {
165                            if (value instanceof Long[]) {
166                                    Long[] values = (Long[])value;
167    
168                                    if (ArrayUtil.isEmpty(values)) {
169                                            return;
170                                    }
171    
172                                    TermsFilter userGroupsTermsFilter = new TermsFilter("groupIds");
173    
174                                    userGroupsTermsFilter.addValues(
175                                            ArrayUtil.toStringArray(values));
176    
177                                    contextFilter.add(
178                                            userGroupsTermsFilter, BooleanClauseOccur.MUST);
179                            }
180                            else {
181                                    contextFilter.addRequiredTerm(
182                                            "groupIds", String.valueOf(value));
183                            }
184                    }
185                    else if (key.equals("usersOrgs")) {
186                            if (value instanceof Long[]) {
187                                    Long[] values = (Long[])value;
188    
189                                    if (ArrayUtil.isEmpty(values)) {
190                                            return;
191                                    }
192    
193                                    TermsFilter organizationsTermsFilter = new TermsFilter(
194                                            "organizationIds");
195                                    TermsFilter ancestorOrgsTermsFilter = new TermsFilter(
196                                            "ancestorOrganizationIds");
197    
198                                    String[] organizationIdsStrings = ArrayUtil.toStringArray(
199                                            values);
200    
201                                    ancestorOrgsTermsFilter.addValues(organizationIdsStrings);
202    
203                                    organizationsTermsFilter.addValues(organizationIdsStrings);
204    
205                                    BooleanFilter userOrgsBooleanFilter = new BooleanFilter();
206    
207                                    userOrgsBooleanFilter.add(ancestorOrgsTermsFilter);
208                                    userOrgsBooleanFilter.add(organizationsTermsFilter);
209    
210                                    contextFilter.add(
211                                            userOrgsBooleanFilter, BooleanClauseOccur.MUST);
212                            }
213                            else {
214                                    contextFilter.addRequiredTerm(
215                                            "organizationIds", String.valueOf(value));
216                            }
217                    }
218                    else if (key.equals("usersOrgsCount")) {
219                            contextFilter.addRequiredTerm(
220                                    "organizationCount", String.valueOf(value));
221                    }
222                    else if (key.equals("usersRoles")) {
223                            contextFilter.addRequiredTerm("roleIds", String.valueOf(value));
224                    }
225                    else if (key.equals("usersTeams")) {
226                            contextFilter.addRequiredTerm("teamIds", String.valueOf(value));
227                    }
228                    else if (key.equals("usersUserGroups")) {
229                            contextFilter.addRequiredTerm(
230                                    "userGroupIds", String.valueOf(value));
231                    }
232            }
233    
234            @Override
235            protected void doDelete(User user) throws Exception {
236                    deleteDocument(user.getCompanyId(), user.getUserId());
237    
238                    Indexer<Contact> indexer = IndexerRegistryUtil.nullSafeGetIndexer(
239                            Contact.class);
240    
241                    Contact contact = new ContactImpl();
242    
243                    contact.setContactId(user.getContactId());
244                    contact.setCompanyId(user.getCompanyId());
245    
246                    indexer.delete(contact);
247            }
248    
249            @Override
250            protected Document doGetDocument(User user) throws Exception {
251                    Document document = getBaseModelDocument(CLASS_NAME, user);
252    
253                    long[] organizationIds = user.getOrganizationIds();
254    
255                    document.addKeyword(Field.COMPANY_ID, user.getCompanyId());
256                    document.addKeyword(Field.GROUP_ID, user.getGroupIds());
257                    document.addDate(Field.MODIFIED_DATE, user.getModifiedDate());
258                    document.addKeyword(Field.SCOPE_GROUP_ID, user.getGroupIds());
259                    document.addKeyword(Field.STATUS, user.getStatus());
260                    document.addKeyword(Field.USER_ID, user.getUserId());
261                    document.addKeyword(Field.USER_NAME, user.getFullName());
262    
263                    document.addKeyword(
264                            "ancestorOrganizationIds",
265                            getAncestorOrganizationIds(user.getOrganizationIds()));
266                    document.addText("emailAddress", user.getEmailAddress());
267                    document.addText("firstName", user.getFirstName());
268                    document.addText("fullName", user.getFullName());
269                    document.addKeyword("groupIds", user.getGroupIds());
270                    document.addText("jobTitle", user.getJobTitle());
271                    document.addText("lastName", user.getLastName());
272                    document.addText("middleName", user.getMiddleName());
273                    document.addKeyword("organizationIds", organizationIds);
274                    document.addKeyword(
275                            "organizationCount", String.valueOf(organizationIds.length));
276                    document.addKeyword("roleIds", user.getRoleIds());
277                    document.addText("screenName", user.getScreenName());
278                    document.addKeyword("teamIds", user.getTeamIds());
279                    document.addKeyword("userGroupIds", user.getUserGroupIds());
280    
281                    populateAddresses(document, user.getAddresses(), 0, 0);
282    
283                    return document;
284            }
285    
286            @Override
287            protected String doGetSortField(String orderByCol) {
288                    if (orderByCol.equals("email-address")) {
289                            return "emailAddress";
290                    }
291                    else if (orderByCol.equals("first-name")) {
292                            return "firstName";
293                    }
294                    else if (orderByCol.equals("job-title")) {
295                            return "jobTitle";
296                    }
297                    else if (orderByCol.equals("last-name")) {
298                            return "lastName";
299                    }
300                    else if (orderByCol.equals("screen-name")) {
301                            return "screenName";
302                    }
303                    else {
304                            return orderByCol;
305                    }
306            }
307    
308            @Override
309            protected Summary doGetSummary(
310                    Document document, Locale locale, String snippet,
311                    PortletRequest portletRequest, PortletResponse portletResponse) {
312    
313                    String firstName = document.get("firstName");
314                    String middleName = document.get("middleName");
315                    String lastName = document.get("lastName");
316    
317                    FullNameGenerator fullNameGenerator =
318                            FullNameGeneratorFactory.getInstance();
319    
320                    String title = fullNameGenerator.getFullName(
321                            firstName, middleName, lastName);
322    
323                    String content = null;
324    
325                    return new Summary(title, content);
326            }
327    
328            @Override
329            protected void doReindex(String className, long classPK) throws Exception {
330                    User user = UserLocalServiceUtil.getUserById(classPK);
331    
332                    doReindex(user);
333            }
334    
335            @Override
336            protected void doReindex(String[] ids) throws Exception {
337                    long companyId = GetterUtil.getLong(ids[0]);
338    
339                    reindexUsers(companyId);
340            }
341    
342            @Override
343            protected void doReindex(User user) throws Exception {
344                    if (user.isDefaultUser()) {
345                            return;
346                    }
347    
348                    Document document = getDocument(user);
349    
350                    IndexWriterHelperUtil.updateDocument(
351                            getSearchEngineId(), user.getCompanyId(), document,
352                            isCommitImmediately());
353    
354                    Indexer<Contact> indexer = IndexerRegistryUtil.nullSafeGetIndexer(
355                            Contact.class);
356    
357                    try {
358                            indexer.reindex(user.getContact());
359                    }
360                    catch (NoSuchContactException nsce) {
361    
362                            // This is a temporary workaround for LPS-46825
363    
364                            if (_log.isDebugEnabled()) {
365                                    _log.debug(nsce, nsce);
366                            }
367                    }
368            }
369    
370            protected long[] getAncestorOrganizationIds(long[] organizationIds)
371                    throws Exception {
372    
373                    Set<Long> ancestorOrganizationIds = new HashSet<>();
374    
375                    for (long organizationId : organizationIds) {
376                            Organization organization =
377                                    OrganizationLocalServiceUtil.getOrganization(organizationId);
378    
379                            for (long ancestorOrganizationId :
380                                            organization.getAncestorOrganizationIds()) {
381    
382                                    ancestorOrganizationIds.add(ancestorOrganizationId);
383                            }
384                    }
385    
386                    return ArrayUtil.toLongArray(ancestorOrganizationIds);
387            }
388    
389            protected void reindexUsers(long companyId) throws PortalException {
390                    final IndexableActionableDynamicQuery indexableActionableDynamicQuery =
391                            UserLocalServiceUtil.getIndexableActionableDynamicQuery();
392    
393                    indexableActionableDynamicQuery.setCompanyId(companyId);
394                    indexableActionableDynamicQuery.setPerformActionMethod(
395                            new ActionableDynamicQuery.PerformActionMethod<User>() {
396    
397                                    @Override
398                                    public void performAction(User user) {
399                                            if (!user.isDefaultUser()) {
400                                                    try {
401                                                            Document document = getDocument(user);
402    
403                                                            indexableActionableDynamicQuery.addDocuments(
404                                                                    document);
405                                                    }
406                                                    catch (PortalException pe) {
407                                                            if (_log.isWarnEnabled()) {
408                                                                    _log.warn(
409                                                                            "Unable to index user " + user.getUserId(),
410                                                                            pe);
411                                                            }
412                                                    }
413                                            }
414                                    }
415    
416                            });
417                    indexableActionableDynamicQuery.setSearchEngineId(getSearchEngineId());
418    
419                    indexableActionableDynamicQuery.performActions();
420            }
421    
422            private static final Log _log = LogFactoryUtil.getLog(UserIndexer.class);
423    
424    }