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