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