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