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