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.portal.service.impl;
016    
017    import com.liferay.portal.DuplicateUserGroupException;
018    import com.liferay.portal.NoSuchUserGroupException;
019    import com.liferay.portal.RequiredUserGroupException;
020    import com.liferay.portal.UserGroupNameException;
021    import com.liferay.portal.kernel.dao.orm.QueryUtil;
022    import com.liferay.portal.kernel.exception.PortalException;
023    import com.liferay.portal.kernel.exception.SystemException;
024    import com.liferay.portal.kernel.search.BaseModelSearchResult;
025    import com.liferay.portal.kernel.search.Hits;
026    import com.liferay.portal.kernel.search.Indexer;
027    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
028    import com.liferay.portal.kernel.search.QueryConfig;
029    import com.liferay.portal.kernel.search.SearchContext;
030    import com.liferay.portal.kernel.search.SearchException;
031    import com.liferay.portal.kernel.search.Sort;
032    import com.liferay.portal.kernel.systemevent.SystemEvent;
033    import com.liferay.portal.kernel.util.CharPool;
034    import com.liferay.portal.kernel.util.Constants;
035    import com.liferay.portal.kernel.util.GetterUtil;
036    import com.liferay.portal.kernel.util.MapUtil;
037    import com.liferay.portal.kernel.util.OrderByComparator;
038    import com.liferay.portal.kernel.util.SetUtil;
039    import com.liferay.portal.kernel.util.StringPool;
040    import com.liferay.portal.kernel.util.Validator;
041    import com.liferay.portal.kernel.workflow.WorkflowConstants;
042    import com.liferay.portal.model.Group;
043    import com.liferay.portal.model.GroupConstants;
044    import com.liferay.portal.model.ResourceConstants;
045    import com.liferay.portal.model.SystemEventConstants;
046    import com.liferay.portal.model.Team;
047    import com.liferay.portal.model.User;
048    import com.liferay.portal.model.UserGroup;
049    import com.liferay.portal.model.UserGroupConstants;
050    import com.liferay.portal.security.auth.PrincipalThreadLocal;
051    import com.liferay.portal.security.exportimport.UserGroupImportTransactionThreadLocal;
052    import com.liferay.portal.security.permission.PermissionCacheUtil;
053    import com.liferay.portal.service.ServiceContext;
054    import com.liferay.portal.service.base.UserGroupLocalServiceBaseImpl;
055    import com.liferay.portal.util.PropsValues;
056    import com.liferay.portlet.exportimport.configuration.ExportImportConfigurationConstants;
057    import com.liferay.portlet.exportimport.configuration.ExportImportConfigurationSettingsMapFactory;
058    import com.liferay.portlet.exportimport.lar.ExportImportHelperUtil;
059    import com.liferay.portlet.exportimport.lar.PortletDataHandlerKeys;
060    import com.liferay.portlet.exportimport.lar.UserIdStrategy;
061    import com.liferay.portlet.exportimport.model.ExportImportConfiguration;
062    import com.liferay.portlet.usersadmin.util.UsersAdminUtil;
063    
064    import java.io.File;
065    import java.io.Serializable;
066    
067    import java.util.ArrayList;
068    import java.util.Collections;
069    import java.util.HashMap;
070    import java.util.LinkedHashMap;
071    import java.util.List;
072    import java.util.Map;
073    import java.util.Set;
074    
075    /**
076     * Provides the local service for accessing, adding, deleting, and updating user
077     * groups.
078     *
079     * @author Charles May
080     */
081    public class UserGroupLocalServiceImpl extends UserGroupLocalServiceBaseImpl {
082    
083            /**
084             * Adds the user groups to the group.
085             *
086             * @param groupId the primary key of the group
087             * @param userGroupIds the primary keys of the user groups
088             */
089            @Override
090            public void addGroupUserGroups(long groupId, long[] userGroupIds) {
091                    groupPersistence.addUserGroups(groupId, userGroupIds);
092    
093                    PermissionCacheUtil.clearCache();
094            }
095    
096            /**
097             * Adds the user groups to the team.
098             *
099             * @param teamId the primary key of the team
100             * @param userGroupIds the primary keys of the user groups
101             */
102            @Override
103            public void addTeamUserGroups(long teamId, long[] userGroupIds) {
104                    teamPersistence.addUserGroups(teamId, userGroupIds);
105    
106                    PermissionCacheUtil.clearCache();
107            }
108    
109            /**
110             * Adds a user group.
111             *
112             * <p>
113             * This method handles the creation and bookkeeping of the user group,
114             * including its resources, metadata, and internal data structures. It is
115             * not necessary to make subsequent calls to setup default groups and
116             * resources for the user group.
117             * </p>
118             *
119             * @param      userId the primary key of the user
120             * @param      companyId the primary key of the user group's company
121             * @param      name the user group's name
122             * @param      description the user group's description
123             * @return     the user group
124             * @throws     PortalException if the user group's information was invalid
125             * @deprecated As of 6.2.0, replaced by {@link #addUserGroup(long, long,
126             *             String, String, ServiceContext)}
127             */
128            @Deprecated
129            @Override
130            public UserGroup addUserGroup(
131                            long userId, long companyId, String name, String description)
132                    throws PortalException {
133    
134                    return addUserGroup(userId, companyId, name, description, null);
135            }
136    
137            /**
138             * Adds a user group.
139             *
140             * <p>
141             * This method handles the creation and bookkeeping of the user group,
142             * including its resources, metadata, and internal data structures. It is
143             * not necessary to make subsequent calls to setup default groups and
144             * resources for the user group.
145             * </p>
146             *
147             * @param  userId the primary key of the user
148             * @param  companyId the primary key of the user group's company
149             * @param  name the user group's name
150             * @param  description the user group's description
151             * @param  serviceContext the service context to be applied (optionally
152             *         <code>null</code>). Can set expando bridge attributes for the
153             *         user group.
154             * @return the user group
155             * @throws PortalException if the user group's information was invalid
156             */
157            @Override
158            public UserGroup addUserGroup(
159                            long userId, long companyId, String name, String description,
160                            ServiceContext serviceContext)
161                    throws PortalException {
162    
163                    // User group
164    
165                    validate(0, companyId, name);
166    
167                    User user = userPersistence.findByPrimaryKey(userId);
168    
169                    long userGroupId = counterLocalService.increment();
170    
171                    UserGroup userGroup = userGroupPersistence.create(userGroupId);
172    
173                    if (serviceContext != null) {
174                            userGroup.setUuid(serviceContext.getUuid());
175                    }
176    
177                    userGroup.setCompanyId(companyId);
178                    userGroup.setUserId(user.getUserId());
179                    userGroup.setUserName(user.getFullName());
180                    userGroup.setParentUserGroupId(
181                            UserGroupConstants.DEFAULT_PARENT_USER_GROUP_ID);
182                    userGroup.setName(name);
183                    userGroup.setDescription(description);
184                    userGroup.setAddedByLDAPImport(
185                            UserGroupImportTransactionThreadLocal.isOriginatesFromImport());
186                    userGroup.setExpandoBridgeAttributes(serviceContext);
187    
188                    userGroupPersistence.update(userGroup);
189    
190                    // Group
191    
192                    groupLocalService.addGroup(
193                            userId, GroupConstants.DEFAULT_PARENT_GROUP_ID,
194                            UserGroup.class.getName(), userGroup.getUserGroupId(),
195                            GroupConstants.DEFAULT_LIVE_GROUP_ID,
196                            getLocalizationMap(String.valueOf(userGroupId)), null, 0, true,
197                            GroupConstants.DEFAULT_MEMBERSHIP_RESTRICTION, null, false, true,
198                            null);
199    
200                    // Resources
201    
202                    resourceLocalService.addResources(
203                            companyId, 0, userId, UserGroup.class.getName(),
204                            userGroup.getUserGroupId(), false, false, false);
205    
206                    // Indexer
207    
208                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
209                            UserGroup.class);
210    
211                    indexer.reindex(userGroup);
212    
213                    return userGroup;
214            }
215    
216            /**
217             * Clears all associations between the user and its user groups and clears
218             * the permissions cache.
219             *
220             * <p>
221             * This method is called from {@link #deleteUserGroup(UserGroup)}.
222             * </p>
223             *
224             * @param userId the primary key of the user
225             */
226            @Override
227            public void clearUserUserGroups(long userId) {
228                    userPersistence.clearUserGroups(userId);
229    
230                    PermissionCacheUtil.clearCache(userId);
231            }
232    
233            /**
234             * Copies the user group's layout to the user.
235             *
236             * @param      userGroupId the primary key of the user group
237             * @param      userId the primary key of the user
238             * @throws     PortalException if a user with the primary key could not be
239             *             found or if a portal exception occurred
240             * @deprecated As of 6.2.0
241             */
242            @Deprecated
243            @Override
244            public void copyUserGroupLayouts(long userGroupId, long userId)
245                    throws PortalException {
246    
247                    Map<String, String[]> parameterMap = getLayoutTemplatesParameters();
248    
249                    File[] files = exportLayouts(userGroupId, parameterMap);
250    
251                    try {
252                            importLayouts(userId, parameterMap, files[0], files[1]);
253                    }
254                    finally {
255                            if (files[0] != null) {
256                                    files[0].delete();
257                            }
258    
259                            if (files[1] != null) {
260                                    files[1].delete();
261                            }
262                    }
263            }
264    
265            /**
266             * Copies the user group's layouts to the users who are not already members
267             * of the user group.
268             *
269             * @param      userGroupId the primary key of the user group
270             * @param      userIds the primary keys of the users
271             * @throws     PortalException if any one of the users could not be found or
272             *             if a portal exception occurred
273             * @deprecated As of 6.1.0
274             */
275            @Deprecated
276            @Override
277            public void copyUserGroupLayouts(long userGroupId, long[] userIds)
278                    throws PortalException {
279    
280                    Map<String, String[]> parameterMap = getLayoutTemplatesParameters();
281    
282                    File[] files = exportLayouts(userGroupId, parameterMap);
283    
284                    try {
285                            for (long userId : userIds) {
286                                    if (!userGroupPersistence.containsUser(userGroupId, userId)) {
287                                            importLayouts(userId, parameterMap, files[0], files[1]);
288                                    }
289                            }
290                    }
291                    finally {
292                            if (files[0] != null) {
293                                    files[0].delete();
294                            }
295    
296                            if (files[1] != null) {
297                                    files[1].delete();
298                            }
299                    }
300            }
301    
302            /**
303             * Copies the user groups' layouts to the user.
304             *
305             * @param      userGroupIds the primary keys of the user groups
306             * @param      userId the primary key of the user
307             * @throws     PortalException if a user with the primary key could not be
308             *             found or if a portal exception occurred
309             * @deprecated As of 6.1.0
310             */
311            @Deprecated
312            @Override
313            public void copyUserGroupLayouts(long[] userGroupIds, long userId)
314                    throws PortalException {
315    
316                    for (long userGroupId : userGroupIds) {
317                            if (!userGroupPersistence.containsUser(userGroupId, userId)) {
318                                    copyUserGroupLayouts(userGroupId, userId);
319                            }
320                    }
321            }
322    
323            /**
324             * Deletes the user group.
325             *
326             * @param  userGroupId the primary key of the user group
327             * @return the deleted user group
328             * @throws PortalException if a user group with the primary key could not be
329             *         found or if the user group had a workflow in approved status
330             */
331            @Override
332            public UserGroup deleteUserGroup(long userGroupId) throws PortalException {
333                    UserGroup userGroup = userGroupPersistence.findByPrimaryKey(
334                            userGroupId);
335    
336                    return userGroupLocalService.deleteUserGroup(userGroup);
337            }
338    
339            /**
340             * Deletes the user group.
341             *
342             * @param  userGroup the user group
343             * @return the deleted user group
344             * @throws PortalException if the organization had a workflow in approved
345             *         status
346             */
347            @Override
348            @SystemEvent(
349                    action = SystemEventConstants.ACTION_SKIP,
350                    type = SystemEventConstants.TYPE_DELETE
351            )
352            public UserGroup deleteUserGroup(UserGroup userGroup)
353                    throws PortalException {
354    
355                    int count = userLocalService.getUserGroupUsersCount(
356                            userGroup.getUserGroupId(), WorkflowConstants.STATUS_APPROVED);
357    
358                    if (count > 0) {
359                            throw new RequiredUserGroupException();
360                    }
361    
362                    // Expando
363    
364                    expandoRowLocalService.deleteRows(userGroup.getUserGroupId());
365    
366                    // Users
367    
368                    clearUserUserGroups(userGroup.getUserId());
369    
370                    // Group
371    
372                    Group group = userGroup.getGroup();
373    
374                    groupLocalService.deleteGroup(group);
375    
376                    // User group roles
377    
378                    userGroupGroupRoleLocalService.deleteUserGroupGroupRolesByUserGroupId(
379                            userGroup.getUserGroupId());
380    
381                    // Resources
382    
383                    resourceLocalService.deleteResource(
384                            userGroup.getCompanyId(), UserGroup.class.getName(),
385                            ResourceConstants.SCOPE_INDIVIDUAL, userGroup.getUserGroupId());
386    
387                    // User group
388    
389                    userGroupPersistence.remove(userGroup);
390    
391                    // Permission cache
392    
393                    PermissionCacheUtil.clearCache();
394    
395                    return userGroup;
396            }
397    
398            @Override
399            public void deleteUserGroups(long companyId) throws PortalException {
400                    List<UserGroup> userGroups = userGroupPersistence.findByCompanyId(
401                            companyId);
402    
403                    for (UserGroup userGroup : userGroups) {
404                            userGroupLocalService.deleteUserGroup(userGroup);
405                    }
406            }
407    
408            @Override
409            public UserGroup fetchUserGroup(long companyId, String name) {
410                    return userGroupPersistence.fetchByC_N(companyId, name);
411            }
412    
413            @Override
414            public List<UserGroup> getGroupUserUserGroups(long groupId, long userId)
415                    throws PortalException {
416    
417                    long[] groupUserGroupIds = groupPersistence.getUserGroupPrimaryKeys(
418                            groupId);
419    
420                    if (groupUserGroupIds.length == 0) {
421                            return Collections.emptyList();
422                    }
423    
424                    long[] userUserGroupIds = userPersistence.getUserGroupPrimaryKeys(
425                            userId);
426    
427                    if (userUserGroupIds.length == 0) {
428                            return Collections.emptyList();
429                    }
430    
431                    Set<Long> userGroupIds = SetUtil.intersect(
432                            groupUserGroupIds, userUserGroupIds);
433    
434                    if (userGroupIds.isEmpty()) {
435                            return Collections.emptyList();
436                    }
437    
438                    List<UserGroup> userGroups = new ArrayList<>(userGroupIds.size());
439    
440                    for (Long userGroupId : userGroupIds) {
441                            userGroups.add(userGroupPersistence.findByPrimaryKey(userGroupId));
442                    }
443    
444                    return userGroups;
445            }
446    
447            /**
448             * Returns the user group with the name.
449             *
450             * @param  companyId the primary key of the user group's company
451             * @param  name the user group's name
452             * @return Returns the user group with the name
453             * @throws PortalException if a user group with the name could not be found
454             */
455            @Override
456            public UserGroup getUserGroup(long companyId, String name)
457                    throws PortalException {
458    
459                    return userGroupPersistence.findByC_N(companyId, name);
460            }
461    
462            /**
463             * Returns all the user groups belonging to the company.
464             *
465             * @param  companyId the primary key of the user groups' company
466             * @return the user groups belonging to the company
467             */
468            @Override
469            public List<UserGroup> getUserGroups(long companyId) {
470                    return userGroupPersistence.findByCompanyId(companyId);
471            }
472    
473            /**
474             * Returns all the user groups with the primary keys.
475             *
476             * @param  userGroupIds the primary keys of the user groups
477             * @return the user groups with the primary keys
478             * @throws PortalException if any one of the user groups could not be found
479             */
480            @Override
481            public List<UserGroup> getUserGroups(long[] userGroupIds)
482                    throws PortalException {
483    
484                    List<UserGroup> userGroups = new ArrayList<>(userGroupIds.length);
485    
486                    for (long userGroupId : userGroupIds) {
487                            UserGroup userGroup = getUserGroup(userGroupId);
488    
489                            userGroups.add(userGroup);
490                    }
491    
492                    return userGroups;
493            }
494    
495            /**
496             * Returns an ordered range of all the user groups that match the keywords.
497             *
498             * <p>
499             * Useful when paginating results. Returns a maximum of <code>end -
500             * start</code> instances. <code>start</code> and <code>end</code> are not
501             * primary keys, they are indexes in the result set. Thus, <code>0</code>
502             * refers to the first result in the set. Setting both <code>start</code>
503             * and <code>end</code> to {@link
504             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
505             * result set.
506             * </p>
507             *
508             * @param  companyId the primary key of the user group's company
509             * @param  keywords the keywords (space separated), which may occur in the
510             *         user group's name or description (optionally <code>null</code>)
511             * @param  params the finder params (optionally <code>null</code>). For more
512             *         information see {@link
513             *         com.liferay.portal.service.persistence.UserGroupFinder}
514             * @param  start the lower bound of the range of user groups to return
515             * @param  end the upper bound of the range of user groups to return (not
516             *         inclusive)
517             * @param  obc the comparator to order the user groups (optionally
518             *         <code>null</code>)
519             * @return the matching user groups ordered by comparator <code>obc</code>
520             * @see    com.liferay.portal.service.persistence.UserGroupFinder
521             */
522            @Override
523            public List<UserGroup> search(
524                    long companyId, String keywords, LinkedHashMap<String, Object> params,
525                    int start, int end, OrderByComparator<UserGroup> obc) {
526    
527                    return userGroupFinder.findByKeywords(
528                            companyId, keywords, params, start, end, obc);
529            }
530    
531            /**
532             * Returns an ordered range of all the user groups that match the keywords,
533             * using the indexer. It is preferable to use this method instead of the
534             * non-indexed version whenever possible for performance reasons.
535             *
536             * <p>
537             * Useful when paginating results. Returns a maximum of <code>end -
538             * start</code> instances. <code>start</code> and <code>end</code> are not
539             * primary keys, they are indexes in the result set. Thus, <code>0</code>
540             * refers to the first result in the set. Setting both <code>start</code>
541             * and <code>end</code> to {@link
542             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
543             * result set.
544             * </p>
545             *
546             * @param  companyId the primary key of the user group's company
547             * @param  keywords the keywords (space separated), which may occur in the
548             *         user group's name or description (optionally <code>null</code>)
549             * @param  params the finder params (optionally <code>null</code>). For more
550             *         information see {@link
551             *         com.liferay.portlet.usergroupsadmin.util.UserGroupIndexer}
552             * @param  start the lower bound of the range of user groups to return
553             * @param  end the upper bound of the range of user groups to return (not
554             *         inclusive)
555             * @param  sort the field and direction by which to sort (optionally
556             *         <code>null</code>)
557             * @return the matching user groups ordered by sort
558             * @see    com.liferay.portlet.usergroupsadmin.util.UserGroupIndexer
559             */
560            @Override
561            public Hits search(
562                    long companyId, String keywords, LinkedHashMap<String, Object> params,
563                    int start, int end, Sort sort) {
564    
565                    String name = null;
566                    String description = null;
567                    boolean andOperator = false;
568    
569                    if (Validator.isNotNull(keywords)) {
570                            name = keywords;
571                            description = keywords;
572                    }
573                    else {
574                            andOperator = true;
575                    }
576    
577                    if (params != null) {
578                            params.put("keywords", keywords);
579                    }
580    
581                    return search(
582                            companyId, name, description, params, andOperator, start, end,
583                            sort);
584            }
585    
586            /**
587             * Returns an ordered range of all the user groups that match the name and
588             * description.
589             *
590             * <p>
591             * Useful when paginating results. Returns a maximum of <code>end -
592             * start</code> instances. <code>start</code> and <code>end</code> are not
593             * primary keys, they are indexes in the result set. Thus, <code>0</code>
594             * refers to the first result in the set. Setting both <code>start</code>
595             * and <code>end</code> to {@link
596             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
597             * result set.
598             * </p>
599             *
600             * @param  companyId the primary key of the user group's company
601             * @param  name the user group's name (optionally <code>null</code>)
602             * @param  description the user group's description (optionally
603             *         <code>null</code>)
604             * @param  params the finder params (optionally <code>null</code>). For more
605             *         information see {@link
606             *         com.liferay.portal.service.persistence.UserGroupFinder}
607             * @param  andOperator whether every field must match its keywords or just
608             *         one field
609             * @param  start the lower bound of the range of user groups to return
610             * @param  end the upper bound of the range of user groups to return (not
611             *         inclusive)
612             * @param  obc the comparator to order the user groups (optionally
613             *         <code>null</code>)
614             * @return the matching user groups ordered by comparator <code>obc</code>
615             * @see    com.liferay.portal.service.persistence.UserGroupFinder
616             */
617            @Override
618            public List<UserGroup> search(
619                    long companyId, String name, String description,
620                    LinkedHashMap<String, Object> params, boolean andOperator, int start,
621                    int end, OrderByComparator<UserGroup> obc) {
622    
623                    return userGroupFinder.findByC_N_D(
624                            companyId, name, description, params, andOperator, start, end, obc);
625            }
626    
627            /**
628             * Returns an ordered range of all the user groups that match the name and
629             * description. It is preferable to use this method instead of the
630             * non-indexed version whenever possible for performance reasons.
631             *
632             * <p>
633             * Useful when paginating results. Returns a maximum of <code>end -
634             * start</code> instances. <code>start</code> and <code>end</code> are not
635             * primary keys, they are indexes in the result set. Thus, <code>0</code>
636             * refers to the first result in the set. Setting both <code>start</code>
637             * and <code>end</code> to {@link
638             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
639             * result set.
640             * </p>
641             *
642             * @param  companyId the primary key of the user group's company
643             * @param  name the user group's name (optionally <code>null</code>)
644             * @param  description the user group's description (optionally
645             *         <code>null</code>)
646             * @param  params the finder params (optionally <code>null</code>). For more
647             *         information see {@link
648             *         com.liferay.portlet.usergroupsadmin.util.UserGroupIndexer}
649             * @param  andSearch whether every field must match its keywords or just one
650             *         field
651             * @param  start the lower bound of the range of user groups to return
652             * @param  end the upper bound of the range of user groups to return (not
653             *         inclusive)
654             * @param  sort the field and direction by which to sort (optionally
655             *         <code>null</code>)
656             * @return the matching user groups ordered by sort
657             * @see    com.liferay.portal.service.persistence.UserGroupFinder
658             */
659            @Override
660            public Hits search(
661                    long companyId, String name, String description,
662                    LinkedHashMap<String, Object> params, boolean andSearch, int start,
663                    int end, Sort sort) {
664    
665                    try {
666                            Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
667                                    UserGroup.class);
668    
669                            SearchContext searchContext = buildSearchContext(
670                                    companyId, name, description, params, andSearch, start, end,
671                                    sort);
672    
673                            return indexer.search(searchContext);
674                    }
675                    catch (Exception e) {
676                            throw new SystemException(e);
677                    }
678            }
679    
680            /**
681             * Returns the number of user groups that match the keywords
682             *
683             * @param  companyId the primary key of the user group's company
684             * @param  keywords the keywords (space separated), which may occur in the
685             *         user group's name or description (optionally <code>null</code>)
686             * @param  params the finder params (optionally <code>null</code>). For more
687             *         information see {@link
688             *         com.liferay.portal.service.persistence.UserGroupFinder}
689             * @return the number of matching user groups
690             * @see    com.liferay.portal.service.persistence.UserGroupFinder
691             */
692            @Override
693            public int searchCount(
694                    long companyId, String keywords, LinkedHashMap<String, Object> params) {
695    
696                    if (!PropsValues.USER_GROUPS_INDEXER_ENABLED ||
697                            !PropsValues.USER_GROUPS_SEARCH_WITH_INDEX ||
698                            isUseCustomSQL(params)) {
699    
700                            return userGroupFinder.countByKeywords(companyId, keywords, params);
701                    }
702    
703                    String name = null;
704                    String description = null;
705                    boolean andOperator = false;
706    
707                    if (Validator.isNotNull(keywords)) {
708                            name = keywords;
709                            description = keywords;
710                    }
711                    else {
712                            andOperator = true;
713                    }
714    
715                    if (params != null) {
716                            params.put("keywords", keywords);
717                    }
718    
719                    try {
720                            Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
721                                    UserGroup.class);
722    
723                            SearchContext searchContext = buildSearchContext(
724                                    companyId, name, description, params, andOperator,
725                                    QueryUtil.ALL_POS, QueryUtil.ALL_POS, null);
726    
727                            return (int)indexer.searchCount(searchContext);
728                    }
729                    catch (Exception e) {
730                            throw new SystemException(e);
731                    }
732            }
733    
734            /**
735             * Returns the number of user groups that match the name and description.
736             *
737             * @param  companyId the primary key of the user group's company
738             * @param  name the user group's name (optionally <code>null</code>)
739             * @param  description the user group's description (optionally
740             *         <code>null</code>)
741             * @param  params the finder params (optionally <code>null</code>). For more
742             *         information see {@link
743             *         com.liferay.portal.service.persistence.UserGroupFinder}
744             * @param  andOperator whether every field must match its keywords or just
745             *         one field
746             * @return the number of matching user groups
747             * @see    com.liferay.portal.service.persistence.UserGroupFinder
748             */
749            @Override
750            public int searchCount(
751                    long companyId, String name, String description,
752                    LinkedHashMap<String, Object> params, boolean andOperator) {
753    
754                    if (!PropsValues.USER_GROUPS_INDEXER_ENABLED ||
755                            !PropsValues.USER_GROUPS_SEARCH_WITH_INDEX ||
756                            isUseCustomSQL(params)) {
757    
758                            return userGroupFinder.countByC_N_D(
759                                    companyId, name, description, params, andOperator);
760                    }
761    
762                    try {
763                            Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
764                                    UserGroup.class);
765    
766                            SearchContext searchContext = buildSearchContext(
767                                    companyId, name, description, params, true, QueryUtil.ALL_POS,
768                                    QueryUtil.ALL_POS, null);
769    
770                            return (int)indexer.searchCount(searchContext);
771                    }
772                    catch (Exception e) {
773                            throw new SystemException(e);
774                    }
775            }
776    
777            @Override
778            public BaseModelSearchResult<UserGroup> searchUserGroups(
779                            long companyId, String keywords,
780                            LinkedHashMap<String, Object> params, int start, int end, Sort sort)
781                    throws PortalException {
782    
783                    String name = null;
784                    String description = null;
785                    boolean andOperator = false;
786    
787                    if (Validator.isNotNull(keywords)) {
788                            name = keywords;
789                            description = keywords;
790                    }
791                    else {
792                            andOperator = true;
793                    }
794    
795                    if (params != null) {
796                            params.put("keywords", keywords);
797                    }
798    
799                    return searchUserGroups(
800                            companyId, name, description, params, andOperator, start, end,
801                            sort);
802            }
803    
804            @Override
805            public BaseModelSearchResult<UserGroup> searchUserGroups(
806                            long companyId, String name, String description,
807                            LinkedHashMap<String, Object> params, boolean andSearch, int start,
808                            int end, Sort sort)
809                    throws PortalException {
810    
811                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
812                            UserGroup.class);
813    
814                    SearchContext searchContext = buildSearchContext(
815                            companyId, name, description, params, andSearch, start, end, sort);
816    
817                    for (int i = 0; i < 10; i++) {
818                            Hits hits = indexer.search(searchContext);
819    
820                            List<UserGroup> userGroups = UsersAdminUtil.getUserGroups(hits);
821    
822                            if (userGroups != null) {
823                                    return new BaseModelSearchResult<>(
824                                            userGroups, hits.getLength());
825                            }
826                    }
827    
828                    throw new SearchException(
829                            "Unable to fix the search index after 10 attempts");
830            }
831    
832            /**
833             * Sets the user groups associated with the user copying the user group
834             * layouts and removing and adding user group associations for the user as
835             * necessary.
836             *
837             * @param  userId the primary key of the user
838             * @param  userGroupIds the primary keys of the user groups
839             * @throws PortalException if a portal exception occurred
840             */
841            @Override
842            public void setUserUserGroups(long userId, long[] userGroupIds)
843                    throws PortalException {
844    
845                    if (PropsValues.USER_GROUPS_COPY_LAYOUTS_TO_USER_PERSONAL_SITE) {
846                            copyUserGroupLayouts(userGroupIds, userId);
847                    }
848    
849                    userPersistence.setUserGroups(userId, userGroupIds);
850    
851                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(User.class);
852    
853                    indexer.reindex(userId);
854    
855                    PermissionCacheUtil.clearCache(userId);
856            }
857    
858            /**
859             * Removes the user groups from the group.
860             *
861             * @param groupId the primary key of the group
862             * @param userGroupIds the primary keys of the user groups
863             */
864            @Override
865            public void unsetGroupUserGroups(long groupId, long[] userGroupIds) {
866                    List<Team> teams = teamPersistence.findByGroupId(groupId);
867    
868                    for (Team team : teams) {
869                            teamPersistence.removeUserGroups(team.getTeamId(), userGroupIds);
870                    }
871    
872                    userGroupGroupRoleLocalService.deleteUserGroupGroupRoles(
873                            userGroupIds, groupId);
874    
875                    groupPersistence.removeUserGroups(groupId, userGroupIds);
876    
877                    PermissionCacheUtil.clearCache();
878            }
879    
880            /**
881             * Removes the user groups from the team.
882             *
883             * @param teamId the primary key of the team
884             * @param userGroupIds the primary keys of the user groups
885             */
886            @Override
887            public void unsetTeamUserGroups(long teamId, long[] userGroupIds) {
888                    teamPersistence.removeUserGroups(teamId, userGroupIds);
889    
890                    PermissionCacheUtil.clearCache();
891            }
892    
893            /**
894             * Updates the user group.
895             *
896             * @param      companyId the primary key of the user group's company
897             * @param      userGroupId the primary key of the user group
898             * @param      name the user group's name
899             * @param      description the user group's description
900             * @return     the user group
901             * @throws     PortalException if a user group with the primary key could
902             *             not be found or if the new information was invalid
903             * @deprecated As of 6.2.0, replaced by {@link #updateUserGroup(long, long,
904             *             String, String, ServiceContext)}
905             */
906            @Deprecated
907            @Override
908            public UserGroup updateUserGroup(
909                            long companyId, long userGroupId, String name, String description)
910                    throws PortalException {
911    
912                    return updateUserGroup(companyId, userGroupId, name, description, null);
913            }
914    
915            /**
916             * Updates the user group.
917             *
918             * @param  companyId the primary key of the user group's company
919             * @param  userGroupId the primary key of the user group
920             * @param  name the user group's name
921             * @param  description the user group's description
922             * @param  serviceContext the service context to be applied (optionally
923             *         <code>null</code>). Can set expando bridge attributes for the
924             *         user group.
925             * @return the user group
926             * @throws PortalException if a user group with the primary key could not be
927             *         found or if the new information was invalid
928             */
929            @Override
930            public UserGroup updateUserGroup(
931                            long companyId, long userGroupId, String name, String description,
932                            ServiceContext serviceContext)
933                    throws PortalException {
934    
935                    // User group
936    
937                    validate(userGroupId, companyId, name);
938    
939                    UserGroup userGroup = userGroupPersistence.findByPrimaryKey(
940                            userGroupId);
941    
942                    userGroup.setName(name);
943                    userGroup.setDescription(description);
944                    userGroup.setExpandoBridgeAttributes(serviceContext);
945    
946                    userGroupPersistence.update(userGroup);
947    
948                    // Indexer
949    
950                    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(
951                            UserGroup.class);
952    
953                    indexer.reindex(userGroup);
954    
955                    return userGroup;
956            }
957    
958            protected SearchContext buildSearchContext(
959                    long companyId, String name, String description,
960                    LinkedHashMap<String, Object> params, boolean andSearch, int start,
961                    int end, Sort sort) {
962    
963                    SearchContext searchContext = new SearchContext();
964    
965                    searchContext.setAndSearch(andSearch);
966    
967                    Map<String, Serializable> attributes = new HashMap<>();
968    
969                    attributes.put("description", description);
970                    attributes.put("name", name);
971    
972                    searchContext.setAttributes(attributes);
973    
974                    searchContext.setCompanyId(companyId);
975                    searchContext.setEnd(end);
976    
977                    if (params != null) {
978                            String keywords = (String)params.remove("keywords");
979    
980                            if (Validator.isNotNull(keywords)) {
981                                    searchContext.setKeywords(keywords);
982                            }
983                    }
984    
985                    if (sort != null) {
986                            searchContext.setSorts(sort);
987                    }
988    
989                    searchContext.setStart(start);
990    
991                    QueryConfig queryConfig = searchContext.getQueryConfig();
992    
993                    queryConfig.setHighlightEnabled(false);
994                    queryConfig.setScoreEnabled(false);
995    
996                    return searchContext;
997            }
998    
999            protected File[] exportLayouts(
1000                            long userGroupId, Map<String, String[]> parameterMap)
1001                    throws PortalException {
1002    
1003                    File[] files = new File[2];
1004    
1005                    UserGroup userGroup = userGroupPersistence.findByPrimaryKey(
1006                            userGroupId);
1007    
1008                    User user = userLocalService.getUser(
1009                            GetterUtil.getLong(PrincipalThreadLocal.getName()));
1010    
1011                    Group group = userGroup.getGroup();
1012    
1013                    if (userGroup.hasPrivateLayouts()) {
1014                            Map<String, Serializable> settingsMap =
1015                                    ExportImportConfigurationSettingsMapFactory.buildSettingsMap(
1016                                            user.getUserId(), group.getGroupId(), true,
1017                                            ExportImportHelperUtil.getAllLayoutIds(
1018                                                    group.getGroupId(), true),
1019                                            parameterMap, user.getLocale(), user.getTimeZone());
1020    
1021                            ExportImportConfiguration exportImportConfiguration =
1022                                    exportImportConfigurationLocalService.
1023                                            addExportImportConfiguration(
1024                                                    user.getUserId(), group.getGroupId(), StringPool.BLANK,
1025                                                    StringPool.BLANK,
1026                                                    ExportImportConfigurationConstants.TYPE_EXPORT_LAYOUT,
1027                                                    settingsMap, WorkflowConstants.STATUS_DRAFT,
1028                                                    new ServiceContext());
1029    
1030                            files[0] = exportImportLocalService.exportLayoutsAsFile(
1031                                    exportImportConfiguration);
1032                    }
1033    
1034                    if (userGroup.hasPublicLayouts()) {
1035                            Map<String, Serializable> settingsMap =
1036                                    ExportImportConfigurationSettingsMapFactory.buildSettingsMap(
1037                                            user.getUserId(), group.getGroupId(), false,
1038                                            ExportImportHelperUtil.getAllLayoutIds(
1039                                                    group.getGroupId(), false),
1040                                            parameterMap, user.getLocale(), user.getTimeZone());
1041    
1042                            ExportImportConfiguration exportImportConfiguration =
1043                                    exportImportConfigurationLocalService.
1044                                            addExportImportConfiguration(
1045                                                    user.getUserId(), group.getGroupId(), StringPool.BLANK,
1046                                                    StringPool.BLANK,
1047                                                    ExportImportConfigurationConstants.TYPE_EXPORT_LAYOUT,
1048                                                    settingsMap, WorkflowConstants.STATUS_DRAFT,
1049                                                    new ServiceContext());
1050    
1051                            files[1] = exportImportLocalService.exportLayoutsAsFile(
1052                                    exportImportConfiguration);
1053                    }
1054    
1055                    return files;
1056            }
1057    
1058            protected Map<String, String[]> getLayoutTemplatesParameters() {
1059                    Map<String, String[]> parameterMap = new LinkedHashMap<>();
1060    
1061                    parameterMap.put(
1062                            PortletDataHandlerKeys.DATA_STRATEGY,
1063                            new String[] {PortletDataHandlerKeys.DATA_STRATEGY_MIRROR});
1064                    parameterMap.put(
1065                            PortletDataHandlerKeys.DELETE_MISSING_LAYOUTS,
1066                            new String[] {Boolean.FALSE.toString()});
1067                    parameterMap.put(
1068                            PortletDataHandlerKeys.DELETE_PORTLET_DATA,
1069                            new String[] {Boolean.FALSE.toString()});
1070                    parameterMap.put(
1071                            PortletDataHandlerKeys.LAYOUT_SET_SETTINGS,
1072                            new String[] {Boolean.FALSE.toString()});
1073                    parameterMap.put(
1074                            PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE,
1075                            new String[] {
1076                                    PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE_MERGE_BY_LAYOUT_NAME
1077                            });
1078                    parameterMap.put(
1079                            PortletDataHandlerKeys.LOGO,
1080                            new String[] {Boolean.FALSE.toString()});
1081                    parameterMap.put(
1082                            PortletDataHandlerKeys.PERMISSIONS,
1083                            new String[] {Boolean.TRUE.toString()});
1084                    parameterMap.put(
1085                            PortletDataHandlerKeys.PORTLET_CONFIGURATION,
1086                            new String[] {Boolean.TRUE.toString()});
1087                    parameterMap.put(
1088                            PortletDataHandlerKeys.PORTLET_CONFIGURATION_ALL,
1089                            new String[] {Boolean.TRUE.toString()});
1090                    parameterMap.put(
1091                            PortletDataHandlerKeys.PORTLET_DATA,
1092                            new String[] {Boolean.TRUE.toString()});
1093                    parameterMap.put(
1094                            PortletDataHandlerKeys.PORTLET_DATA_ALL,
1095                            new String[] {Boolean.TRUE.toString()});
1096                    parameterMap.put(
1097                            PortletDataHandlerKeys.PORTLET_SETUP_ALL,
1098                            new String[] {Boolean.TRUE.toString()});
1099                    parameterMap.put(
1100                            PortletDataHandlerKeys.PORTLETS_MERGE_MODE,
1101                            new String[] {
1102                                    PortletDataHandlerKeys.PORTLETS_MERGE_MODE_ADD_TO_BOTTOM
1103                            });
1104                    parameterMap.put(
1105                            PortletDataHandlerKeys.THEME_REFERENCE,
1106                            new String[] {Boolean.TRUE.toString()});
1107                    parameterMap.put(
1108                            PortletDataHandlerKeys.UPDATE_LAST_PUBLISH_DATE,
1109                            new String[] {Boolean.FALSE.toString()});
1110                    parameterMap.put(
1111                            PortletDataHandlerKeys.USER_ID_STRATEGY,
1112                            new String[] {UserIdStrategy.CURRENT_USER_ID});
1113    
1114                    return parameterMap;
1115            }
1116    
1117            protected void importLayouts(
1118                            long userId, Map<String, String[]> parameterMap,
1119                            File privateLayoutsFile, File publicLayoutsFile)
1120                    throws PortalException {
1121    
1122                    User user = userPersistence.findByPrimaryKey(userId);
1123    
1124                    long groupId = user.getGroupId();
1125    
1126                    if (privateLayoutsFile != null) {
1127                            Map<String, Serializable> settingsMap =
1128                                    ExportImportConfigurationSettingsMapFactory.
1129                                            buildImportSettingsMap(
1130                                                    user.getUserId(), groupId, true, null, parameterMap,
1131                                                    Constants.IMPORT, user.getLocale(), user.getTimeZone(),
1132                                                    privateLayoutsFile.getName());
1133    
1134                            ExportImportConfiguration exportImportConfiguration =
1135                                    exportImportConfigurationLocalService.
1136                                            addExportImportConfiguration(
1137                                                    user.getUserId(), groupId, StringPool.BLANK,
1138                                                    StringPool.BLANK,
1139                                                    ExportImportConfigurationConstants.TYPE_IMPORT_LAYOUT,
1140                                                    settingsMap, WorkflowConstants.STATUS_DRAFT,
1141                                                    new ServiceContext());
1142    
1143                            exportImportLocalService.importLayouts(
1144                                    exportImportConfiguration, privateLayoutsFile);
1145                    }
1146    
1147                    if (publicLayoutsFile != null) {
1148                            Map<String, Serializable> settingsMap =
1149                                    ExportImportConfigurationSettingsMapFactory.
1150                                            buildImportSettingsMap(
1151                                                    user.getUserId(), groupId, false, null, parameterMap,
1152                                                    Constants.IMPORT, user.getLocale(), user.getTimeZone(),
1153                                                    publicLayoutsFile.getName());
1154    
1155                            ExportImportConfiguration exportImportConfiguration =
1156                                    exportImportConfigurationLocalService.
1157                                            addExportImportConfiguration(
1158                                                    user.getUserId(), groupId, StringPool.BLANK,
1159                                                    StringPool.BLANK,
1160                                                    ExportImportConfigurationConstants.TYPE_IMPORT_LAYOUT,
1161                                                    settingsMap, WorkflowConstants.STATUS_DRAFT,
1162                                                    new ServiceContext());
1163    
1164                            exportImportLocalService.importLayouts(
1165                                    exportImportConfiguration, publicLayoutsFile);
1166                    }
1167            }
1168    
1169            protected boolean isUseCustomSQL(LinkedHashMap<String, Object> params) {
1170                    if (MapUtil.isEmpty(params)) {
1171                            return false;
1172                    }
1173    
1174                    return true;
1175            }
1176    
1177            protected void validate(long userGroupId, long companyId, String name)
1178                    throws PortalException {
1179    
1180                    if (Validator.isNull(name) || (name.indexOf(CharPool.COMMA) != -1) ||
1181                            (name.indexOf(CharPool.STAR) != -1)) {
1182    
1183                            throw new UserGroupNameException();
1184                    }
1185    
1186                    if (Validator.isNumber(name) &&
1187                            !PropsValues.USER_GROUPS_NAME_ALLOW_NUMERIC) {
1188    
1189                            throw new UserGroupNameException();
1190                    }
1191    
1192                    try {
1193                            UserGroup userGroup = userGroupFinder.findByC_N(companyId, name);
1194    
1195                            if (userGroup.getUserGroupId() != userGroupId) {
1196                                    throw new DuplicateUserGroupException(
1197                                            "{userGroupId=" + userGroupId + "}");
1198                            }
1199                    }
1200                    catch (NoSuchUserGroupException nsuge) {
1201                    }
1202            }
1203    
1204    }