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