View Javadoc
1   /*
2    * Copyright (C) 2003-2007 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.social.core.space;
18  
19  import java.util.*;
20  import java.util.concurrent.ConcurrentHashMap;
21  import java.util.concurrent.CopyOnWriteArrayList;
22  import java.util.regex.Pattern;
23  
24  import javax.servlet.http.HttpServletRequest;
25  
26  import org.exoplatform.application.registry.Application;
27  import org.exoplatform.application.registry.ApplicationCategory;
28  import org.exoplatform.application.registry.ApplicationRegistryService;
29  import org.exoplatform.commons.chromattic.ChromatticManager;
30  import org.exoplatform.commons.chromattic.Synchronization;
31  import org.exoplatform.commons.utils.ListAccess;
32  import org.exoplatform.container.ExoContainer;
33  import org.exoplatform.container.ExoContainerContext;
34  import org.exoplatform.container.PortalContainer;
35  import org.exoplatform.container.component.RequestLifeCycle;
36  import org.exoplatform.portal.application.PortalRequestContext;
37  import org.exoplatform.portal.application.RequestNavigationData;
38  import org.exoplatform.portal.config.DataStorage;
39  import org.exoplatform.portal.config.UserACL;
40  import org.exoplatform.portal.config.UserACL.Permission;
41  import org.exoplatform.portal.config.UserPortalConfig;
42  import org.exoplatform.portal.config.UserPortalConfigService;
43  import org.exoplatform.portal.config.model.ApplicationState;
44  import org.exoplatform.portal.config.model.ApplicationType;
45  import org.exoplatform.portal.config.model.Container;
46  import org.exoplatform.portal.config.model.ModelObject;
47  import org.exoplatform.portal.config.model.Page;
48  import org.exoplatform.portal.mop.SiteKey;
49  import org.exoplatform.portal.mop.SiteType;
50  import org.exoplatform.portal.mop.management.operations.navigation.NavigationUtils;
51  import org.exoplatform.portal.mop.navigation.NavigationContext;
52  import org.exoplatform.portal.mop.navigation.NavigationService;
53  import org.exoplatform.portal.mop.navigation.NavigationServiceException;
54  import org.exoplatform.portal.mop.navigation.NavigationState;
55  import org.exoplatform.portal.mop.navigation.NodeContext;
56  import org.exoplatform.portal.mop.navigation.Scope;
57  import org.exoplatform.portal.mop.page.PageContext;
58  import org.exoplatform.portal.mop.page.PageService;
59  import org.exoplatform.portal.mop.user.*;
60  import org.exoplatform.portal.pom.spi.gadget.Gadget;
61  import org.exoplatform.portal.webui.portal.UIPortal;
62  import org.exoplatform.portal.webui.util.Util;
63  import org.exoplatform.portal.webui.workspace.UIPortalApplication;
64  import org.exoplatform.portal.webui.workspace.UIWorkingWorkspace;
65  import org.exoplatform.services.log.ExoLogger;
66  import org.exoplatform.services.log.Log;
67  import org.exoplatform.services.organization.*;
68  import org.exoplatform.services.security.ConversationState;
69  import org.exoplatform.social.common.router.ExoRouter;
70  import org.exoplatform.social.common.router.ExoRouter.Route;
71  import org.exoplatform.social.core.identity.model.Identity;
72  import org.exoplatform.social.core.identity.provider.SpaceIdentityProvider;
73  import org.exoplatform.social.core.manager.IdentityManager;
74  import org.exoplatform.social.core.space.model.Space;
75  import org.exoplatform.social.core.space.spi.SpaceService;
76  import org.exoplatform.webui.application.WebuiRequestContext;
77  
78  import org.apache.commons.lang.StringUtils;
79  import org.gatein.common.i18n.LocalizedString;
80  import org.gatein.common.util.Tools;
81  import org.gatein.pc.api.Portlet;
82  import org.gatein.pc.api.PortletInvoker;
83  import org.gatein.pc.api.info.MetaInfo;
84  import org.gatein.pc.api.info.PortletInfo;
85  
86  import com.ibm.icu.text.Transliterator;
87  
88  /**
89   * SpaceUtils Utility for working with space
90   */
91  public class SpaceUtils {
92    
93    private static final Log LOG = ExoLogger.getLogger(SpaceUtils.class);
94  
95    public static final String SPACE_GROUP = "/spaces";
96  
97    public static final String PLATFORM_USERS_GROUP = "/platform/users";
98  
99    /**
100    * @deprecated Use {@link UserACL#getAdminMSType()} instead. 
101    * Will be removed by 1.2.9
102    */
103   @Deprecated
104   public static final String MANAGER = "manager";
105   
106   public static final String MEMBER = "member";
107 
108   public static final String MENU_CONTAINER = "Menu";
109 
110   public static final String APPLICATION_CONTAINER = "Application";
111 
112   public static final String SPACE_URL = "SPACE_URL";
113   
114   /**
115    * The id of the container in plf.
116    * 
117    * @since 1.2.8
118    */
119   private static final String SPACE_MENU = "SpaceMenu";
120   
121   /**
122    * The id of the container in plf.
123    * 
124    * @since 1.2.8
125    */
126   private static final String SPACE_APPLICATIONS = "SpaceApplications";
127   
128   private static final ConcurrentHashMap<String, Application> appListCache = new ConcurrentHashMap<String,
129           Application>();
130 
131   private static final String REMOTE_CATEGORY_NAME = "remote";
132 
133   private static final Pattern SPACE_NAME_PATTERN = Pattern.compile("^([\\p{L}\\s\\d\'_&]+[\\s]?)+$");
134 
135 
136   // A {@link Transliterator} instance is stateless which has for consequences that it is Thread Safe
137   // and thus can be shared among several threads as mentioned in the javadoc
138   private static final Transliterator ACCENTS_CONVERTER = Transliterator.getInstance("Latin; NFD; [:Nonspacing " +
139           "Mark:] Remove; NFC;");
140 
141   private static String NUMBER_REG_PATTERN = "[0-9]";
142   private static String UNDER_SCORE_STR = "_";
143   private static String SPACE_STR = " ";
144   private static String CURRENT_SPACE = "CurrentSpace";
145 
146   /**
147    * Checks if Space Name is in a valid form or not.
148    *
149    * @param name
150    * @return
151    */
152   public static boolean isValidSpaceName(String name) {
153     return SPACE_NAME_PATTERN.matcher(name).matches();
154   }
155   
156   /**
157    * Creates a new group from an existing group. This new group will get all data from existing group except for group
158    * name
159    *
160    * @param parentGroup
161    * @param existingGroup
162    * @param name
163    * @return new Group
164    * @throws Exception
165    * @deprecated to be removed by 1.2.x
166    */
167   @SuppressWarnings("unchecked")
168   public static Group createGroupFromExistingGroup(Group parentGroup,
169                                                    Group existingGroup,
170                                                    String name) throws Exception {
171     OrganizationService orgSrc = getOrganizationService();
172     GroupHandler groupHandler = orgSrc.getGroupHandler();
173     MembershipHandler memberShipHandler = orgSrc.getMembershipHandler();
174     Group newGroup = groupHandler.createGroupInstance();
175     newGroup.setGroupName(name);
176     newGroup.setLabel(name);
177     newGroup.setDescription(existingGroup.getDescription());
178     groupHandler.addChild(parentGroup, newGroup, true);
179     Collection<Membership> memberShips = memberShipHandler.findMembershipsByGroup(existingGroup);
180     Iterator<Membership> itr = memberShips.iterator();
181     while (itr.hasNext()) {
182       Membership membership = itr.next();
183       User user = orgSrc.getUserHandler().findUserByName(membership.getUserName());
184       MembershipType memberShipType = orgSrc.getMembershipTypeHandler()
185               .findMembershipType(membership.getMembershipType());
186       memberShipHandler.linkMembership(user, newGroup, memberShipType, true);
187     }
188     return newGroup;
189   }
190 
191   /**
192    * Gets applications that a group has right to access
193    *
194    * @param groupId
195    * @return applications
196    * @throws Exception
197    */
198   public static List<Application> getApplications(String groupId) throws Exception {
199 
200     List<Application> list = new CopyOnWriteArrayList<Application>();
201     ApplicationRegistryService appRegistrySrc = getApplicationRegistryService();
202 
203     List<ApplicationCategory> listCategory = appRegistrySrc.getApplicationCategories();
204     Iterator<ApplicationCategory> cateItr = listCategory.iterator();
205     while (cateItr.hasNext()) {
206       ApplicationCategory cate = cateItr.next();
207       if (!hasAccessPermission(cate, groupId)) {
208         cateItr.remove();
209         continue;
210       }
211       ApplicationType<org.exoplatform.portal.pom.spi.portlet.Portlet> portletType = ApplicationType.PORTLET;
212       ApplicationType<Gadget> gadgetType = ApplicationType.GADGET;
213       List<Application> applications = appRegistrySrc.getApplications(cate, portletType, gadgetType);
214       Iterator<Application> appIterator = applications.iterator();
215       while (appIterator.hasNext()) {
216         Application app = appIterator.next();
217         if (!hasAccessPermission(app, groupId)) {
218           appIterator.remove();
219         } else {
220           list.add(app);
221         }
222       }
223     }
224     return list;
225   }
226 
227   /**
228    * Gets appStore of HashMap type with key = ApplicationCategory and value = list of applications. appStore is filter
229    * by access permission from that group; filter by application category access permission and filtered by application
230    * permission.
231    *
232    * @param space
233    * @return appStore
234    * @throws Exception
235    */
236   public static Map<ApplicationCategory, List<Application>> getAppStore(Space space) throws Exception {
237     Map<ApplicationCategory, List<Application>> appStore = new LinkedHashMap<ApplicationCategory, List<Application>>();
238     ApplicationRegistryService appRegistryService = getApplicationRegistryService();
239     String groupId = space.getGroupId();
240     List<ApplicationCategory> categoryList = appRegistryService.getApplicationCategories();
241     Collections.sort(categoryList, new PortletCategoryComparator());
242     Iterator<ApplicationCategory> cateItr = categoryList.iterator();
243     while (cateItr.hasNext()) {
244       ApplicationCategory appCategory = cateItr.next();
245       if (!hasAccessPermission(appCategory, groupId)) {
246         continue;
247       }
248       List<Application> tempAppList = new ArrayList<Application>();
249       List<Application> appList = appCategory.getApplications();
250       Collections.sort(appList, new PortletComparator());
251       Iterator<Application> appItr = appList.iterator();
252       while (appItr.hasNext()) {
253         Application application = appItr.next();
254         if (!hasAccessPermission(application, groupId)) {
255           continue;
256         }
257         tempAppList.add(application);
258       }
259       if (tempAppList.size() > 0) {
260         appStore.put(appCategory, tempAppList);
261       }
262     }
263     return appStore;
264   }
265 
266   /**
267    * Gets application from portal container. This is used to get application when get application by applicationRegistry
268    * return null.
269    *
270    * @param appId
271    * @return An application has name match input appId.
272    * @throws Exception
273    */
274   public static Application getAppFromPortalContainer(String appId) throws Exception {
275     if (appListCache.containsKey(appId)) {
276       return appListCache.get(appId);
277     }
278 
279     ExoContainer container = ExoContainerContext.getCurrentContainer();
280 
281     PortletInvoker portletInvoker = (PortletInvoker) container.getComponentInstance(PortletInvoker.class);
282     Set<Portlet> portlets = portletInvoker.getPortlets();
283     ApplicationRegistryService appRegistryService = getApplicationRegistryService();
284     for (Portlet portlet : portlets) {
285       PortletInfo info = portlet.getInfo();
286       String portletApplicationName = info.getApplicationName();
287       String portletName = info.getName();
288 
289       portletApplicationName = portletApplicationName.replace('/', '_');
290       portletName = portletName.replace('/', '_');
291 
292       if (portletName.equals(appId)) {
293         LocalizedString keywordsLS = info.getMeta().getMetaValue(MetaInfo.KEYWORDS);
294 
295         String[] categoryNames = null;
296         if (keywordsLS != null) {
297           String keywords = keywordsLS.getDefaultString();
298           if (keywords != null && keywords.length() != 0) {
299             categoryNames = keywords.split(",");
300           }
301         }
302 
303         if (categoryNames == null || categoryNames.length == 0) {
304           categoryNames = new String[]{portletApplicationName};
305         }
306 
307         if (portlet.isRemote()) {
308           categoryNames = Tools.appendTo(categoryNames, REMOTE_CATEGORY_NAME);
309         }
310 
311         for (String categoryName : categoryNames) {
312           ApplicationCategory category;
313 
314           categoryName = categoryName.trim();
315 
316           category = appRegistryService.getApplicationCategory(categoryName);
317           if (category == null) {
318             category = new ApplicationCategory();
319             category.setName(categoryName);
320             category.setDisplayName(categoryName);
321           }
322 
323           Application app = appRegistryService.getApplication(categoryName + "/" + portletName);
324           if (app != null) {
325             return app;
326           }
327 
328           // ContentType<?> contentType;
329           String contentId;
330 
331           LocalizedString descriptionLS = portlet.getInfo()
332                   .getMeta()
333                   .getMetaValue(MetaInfo.DESCRIPTION);
334           LocalizedString displayNameLS = portlet.getInfo()
335                   .getMeta()
336                   .getMetaValue(MetaInfo.DISPLAY_NAME);
337 
338           getLocalizedStringValue(descriptionLS, portletName);
339 
340           app = new Application();
341 
342           if (portlet.isRemote()) {
343             // contentType = WSRP.CONTENT_TYPE;
344             contentId = portlet.getContext().getId();
345           } else {
346             contentId = info.getApplicationName() + "/" + info.getName();
347           }
348           app.setType(ApplicationType.PORTLET);
349           app.setContentId(contentId);
350           app.setApplicationName(portletName);
351           app.setCategoryName(categoryName);
352           app.setDisplayName(getLocalizedStringValue(displayNameLS, portletName));
353           app.setDescription(getLocalizedStringValue(descriptionLS, portletName));
354           Application oldApp = appListCache.putIfAbsent(app.getApplicationName(), app);
355           return oldApp == null ? app : oldApp;
356         }
357       }
358     }
359 
360     return null;
361   }
362 
363   /**
364    * PortletCategoryComparator
365    */
366   static class PortletCategoryComparator implements Comparator<ApplicationCategory> {
367     public int compare(ApplicationCategory cat1, ApplicationCategory cat2) {
368       return cat1.getDisplayName(true).compareTo(cat2.getDisplayName(true));
369     }
370   }
371 
372   /**
373    * PortletComparator
374    */
375   static class PortletComparator implements Comparator<Application> {
376     public int compare(Application p1, Application p2) {
377       return p1.getDisplayName().compareTo(p2.getDisplayName());
378     }
379   }
380 
381   /**
382    * Utility for cleaning space name
383    *
384    * @param str
385    * @return cleaned string
386    */
387   public static String cleanString(String str) {
388     if (str == null) {
389       throw new IllegalArgumentException("String argument must not be null.");
390     }
391       
392     str = ACCENTS_CONVERTER.transliterate(str);
393 
394     // the character ? seems to not be changed to d by the transliterate
395     // function
396 
397     StringBuilder cleanedStr = new StringBuilder(str.trim());
398     // delete special character
399     for (int i = 0; i < cleanedStr.length(); i++) {
400       char c = cleanedStr.charAt(i);
401       if (c == ' ') {
402         if (i > 0 && cleanedStr.charAt(i - 1) == '_') {
403           cleanedStr.deleteCharAt(i--);
404         } else {
405           c = '_';
406           cleanedStr.setCharAt(i, c);
407         }
408         continue;
409       }
410 
411       if (!(Character.isLetterOrDigit(c) || c == '_')) {
412         cleanedStr.deleteCharAt(i--);
413         continue;
414       }
415 
416       if (i > 0 && c == '_' && cleanedStr.charAt(i - 1) == '_') {
417         cleanedStr.deleteCharAt(i--);
418       }
419     }
420     return cleanedStr.toString().toLowerCase();
421   }
422 
423   /**
424    * Gets spaceName by portletPreference.
425    *
426    * @return
427    * @deprecated Use {@link #getSpaceUrlByContext()} instead.
428    */
429   public static String getSpaceUrl() {
430     //
431     PortalRequestContext pcontext = Util.getPortalRequestContext();
432     String requestPath = pcontext.getControllerContext().getParameter(RequestNavigationData.REQUEST_PATH);
433     Route route = ExoRouter.route(requestPath);
434     if (route == null) return null;
435 
436     //
437     String spacePrettyName = route.localArgs.get("spacePrettyName");
438     SpaceService spaceService = (SpaceService) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(SpaceService.class);
439     Space space = spaceService.getSpaceByPrettyName(spacePrettyName);
440     
441     return (space != null ? space.getUrl() : null);
442   }
443 
444   /**
445    * Check whether is being in a space context or not.
446    * 
447    * @return
448    * @since 4.0.0-RC2
449    */
450   public static boolean isSpaceContext() {
451     return (getSpaceByContext() != null);
452   }
453 
454   /**
455    * Gets the space url based on the current context.
456    * 
457    * @return
458    * @since 4.0.0-RC2
459    */
460   public static String getSpaceUrlByContext() {
461     Space space = getSpaceByContext();
462     return (space != null ? space.getUrl() : null);
463   }
464 
465   public static Space getSpaceByContext() {
466     //
467     PortalRequestContext pcontext = Util.getPortalRequestContext();
468     Object currentSpaceObject = pcontext.getAttribute(CURRENT_SPACE);
469     if (currentSpaceObject != null) {
470       if(Objects.equals(currentSpaceObject, StringUtils.EMPTY)) {
471         return null;
472       } else {
473         return (Space) currentSpaceObject;
474       }
475     }
476     if (!pcontext.getSiteType().equals(SiteType.GROUP) ||
477         !pcontext.getSiteName().startsWith(SpaceUtils.SPACE_GROUP)) {
478       return null;
479     }
480     String requestPath = pcontext.getControllerContext().getParameter(RequestNavigationData.REQUEST_PATH);
481     Route route = ExoRouter.route(requestPath);
482     if (route == null) {
483       pcontext.setAttribute(CURRENT_SPACE, StringUtils.EMPTY);
484       return null;
485     }
486 
487     //
488     String spacePrettyName = route.localArgs.get("spacePrettyName");
489     SpaceService spaceService = (SpaceService) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(SpaceService.class);
490 
491     Space currentSpace = spaceService.getSpaceByPrettyName(spacePrettyName);
492     pcontext.setAttribute(CURRENT_SPACE, currentSpace);
493     return currentSpace;
494   }
495   /**
496    * Remove pages and group navigation of space when delete space.
497    * 
498    * @param space
499    * @throws Exception
500    * @since 1.2.8
501    */
502   public static void removePagesAndGroupNavigation(Space space) throws Exception {
503     // remove pages
504     DataStorage dataStorage = getDataStorage();
505     String groupId = space.getGroupId();
506     NavigationContext spaceNavCtx = SpaceUtils.getGroupNavigationContext(groupId);
507     // return in case group navigation was removed by portal SOC-548
508     if (spaceNavCtx == null) {
509       return;
510     }
511     NodeContext<NodeContext<?>> homeNodeCtx = SpaceUtils.getHomeNodeWithChildren(spaceNavCtx, groupId);
512 
513     for (NodeContext<?> child : homeNodeCtx.getNodes()) {
514        @SuppressWarnings("unchecked")
515        NodeContext<NodeContext<?>> childNode = (NodeContext<NodeContext<?>>) child;
516        Page page = dataStorage.getPage(childNode.getState().getPageRef().format());
517        dataStorage.remove(page);
518     }
519     
520     
521     
522     // remove group navigation
523     SpaceUtils.removeGroupNavigation(groupId);
524   }
525   
526   /**
527    * change spaceUrl preferences for all applications in a pageNode. This pageNode is the clonedPage of spacetemplate.
528    *
529    * @param spacePageNode
530    * @param space
531    * @throws Exception
532    */
533   @SuppressWarnings("unchecked")
534   public static void changeSpaceUrlPreference(UserNode spacePageNode,
535                                               Space space,
536                                               String newSpaceName) throws Exception {
537     DataStorage dataStorage = getDataStorage();
538     Page page = dataStorage.getPage(spacePageNode.getPageRef().format());
539     
540     ArrayList<ModelObject> pageChildren = page.getChildren();
541     
542     //change menu portlet preference
543     Container menuContainer = findContainerById(pageChildren, MENU_CONTAINER);
544     
545     //This is a workaround for PLF. The workaround should be removed when issue SOC-2074 is resolved.
546     if (menuContainer == null) {
547       menuContainer = findContainerById(pageChildren, SPACE_MENU);
548     }
549 
550     //change applications portlet preference
551     Container applicationContainer = findContainerById(pageChildren, APPLICATION_CONTAINER);
552     if (applicationContainer == null) {
553       applicationContainer = findContainerById(pageChildren, SPACE_APPLICATIONS);
554     }
555   }
556 
557   /**
558    * Change the page title of the application referring to the current spacePageNode
559    * @param spacePageNode
560    * @param newSpaceName
561    * @throws Exception
562   */
563 
564   @SuppressWarnings("unchecked")
565   public static void changeAppPageTitle(UserNode spacePageNode, String newSpaceName) throws Exception {
566     DataStorage dataStorage = getDataStorage();
567     Page page = dataStorage.getPage(spacePageNode.getPageRef().format());
568 
569     ExoContainer container = ExoContainerContext.getCurrentContainer();
570     PageService pageService = (PageService) container.getComponentInstanceOfType(PageService.class);
571     PageContext pageContext = pageService.loadPage(page.getPageKey());
572     if(pageContext != null && pageContext.getState() != null){
573           String dispalyname = pageContext.getState().getDisplayName();
574           if(dispalyname != null && !dispalyname.isEmpty() && dispalyname.indexOf("-") != -1) {
575                 String newPageTitle = newSpaceName+" -"+dispalyname.split("-")[1];
576                 pageContext.setState(pageContext.getState().builder().displayName(newPageTitle).build());
577                 pageService.savePage(pageContext);
578             }
579       }
580   }  
581 
582   /**
583    * Change menu portlet preference.
584    * 
585    * @param menuContainer
586    * @param dataStorage
587    * @param space
588    * @since 1.2.8
589    */
590   public static void changeMenuPortletPreference(Container menuContainer, DataStorage dataStorage, Space space) {
591     org.exoplatform.portal.config.model.Application<org.exoplatform.portal.pom.spi.portlet.Portlet> menuPortlet =
592       (org.exoplatform.portal.config.model.Application<org.exoplatform.portal.pom.spi.portlet.Portlet>) menuContainer
593                 .getChildren()
594                 .get(0);
595 
596       ApplicationState<org.exoplatform.portal.pom.spi.portlet.Portlet> menuState = menuPortlet.getState();
597       org.exoplatform.portal.pom.spi.portlet.Portlet menuPortletPreference;
598       try {
599         menuPortletPreference = dataStorage.load(menuState, ApplicationType.PORTLET);
600         menuPortletPreference.setValue(SPACE_URL, space.getUrl());
601         dataStorage.save(menuState, menuPortletPreference);
602       } catch (Exception e) {
603         LOG.warn("Can not save menu portlet preference!", e);
604       }
605   }
606   
607   /**
608    * Change application portlet preference.
609    * 
610    * @param applicationContainer
611    * @param dataStorage
612    * @param space
613    * @since 1.2.8
614    */
615   public static void changeAppPortletPreference(Container applicationContainer, DataStorage dataStorage, Space space) {
616     try {
617       org.exoplatform.portal.config.model.Application<org.exoplatform.portal.pom.spi.portlet.Portlet> applicationPortlet =
618       (org.exoplatform.portal.config.model.Application<org.exoplatform.portal.pom.spi.portlet.Portlet>) applicationContainer
619               .getChildren()
620               .get(0);
621       ApplicationState<org.exoplatform.portal.pom.spi.portlet.Portlet> appState = applicationPortlet.getState();
622       org.exoplatform.portal.pom.spi.portlet.Portlet appPortletPreference;
623       try {
624         appPortletPreference = dataStorage.load(appState, ApplicationType.PORTLET);
625         if (appPortletPreference == null || appPortletPreference.getPreference(SPACE_URL) == null) {
626           return;
627         } else {
628           appPortletPreference.setValue(SPACE_URL, space.getUrl());
629         }
630         dataStorage.save(appState, appPortletPreference);
631       } catch (Exception e) {
632         LOG.warn("Can not save application portlet preference!", e);
633       }
634     } catch (Exception e) {
635       LOG.warn("Error when change application porltet preference!", e);
636       // ignore it? exception will happen when this is gadgetApplicationType
637     }
638   }
639   
640   /**
641    * end the request and push data to JCR.
642    */
643   public static void endRequest() {
644     RequestLifeCycle.end();
645     //SOC-2124 too long wait for executing these handlers which handles when space created.
646     ExoContainer container = ExoContainerContext.getCurrentContainer();
647     //Need to begin here for RequestLifeCycle.end(); if is not existing, an exception will be appeared. 
648     RequestLifeCycle.begin(container);
649   }
650   
651   /**
652    * end the request and push data to JCR.
653    */
654   public static void endSyn(boolean save) {
655     ExoContainer container = ExoContainerContext.getCurrentContainer();
656     ChromatticManager manager = (ChromatticManager) container.getComponentInstanceOfType(ChromatticManager.class);
657     Synchronization synchronization = manager.getSynchronization();
658     synchronization.setSaveOnClose(save);
659   }
660   /**
661    * Finds container by id
662    *
663    * @param children
664    * @param id
665    * @return
666    */
667   public static Container findContainerById(ArrayList<ModelObject> children, String id) {
668     Container found = null;
669     for (Object obj : children) {
670       if (org.exoplatform.portal.config.model.Application.class.isInstance(obj)) {
671         continue;
672       }
673       Container child = (Container) obj;
674       if (child.getId() == null) {
675         found = findContainerById(child.getChildren(), id);
676         if (found != null) {
677           return found;
678         }
679       } else {
680         if (child.getId().equals(id)) {
681           return child;
682         } else {
683           found = findContainerById(child.getChildren(), id);
684         }
685         if (found != null) {
686           return found;
687         }
688       }
689     }
690     return found;
691   }
692 
693   /**
694    * Utility for setting navigation. Set pageNavigation, if existed in portal navigations, reset; if not, added to
695    * portal navigations.
696    *
697    * @param nav
698    * TODO This method which uses to cache the Navigation. Maybe remove this method because it
699    * uses to cache for UI
700    */
701   public static void setNavigation(UserNavigation nav) {
702     if (nav == null) {
703       return;
704     }
705     WebuiRequestContext context = WebuiRequestContext.getCurrentInstance();
706     if (context == null) {
707       return;
708     }
709     try {
710       UserNode selectedNav = Util.getUIPortal().getSelectedUserNode();
711       if (selectedNav.getId() == nav.getKey().getName()) {
712         Util.getUIPortal().setNavPath(selectedNav);
713       }
714     } catch (Exception e) {
715       LOG.warn(e.getMessage(), e);
716     }
717   }
718 
719   /**
720    * Utility for removing portal navigation.
721    *
722    * @param nav
723    * @throws Exception
724    */
725   public static void removeNavigation(UserNavigation nav) throws Exception {
726     ExoContainer container = ExoContainerContext.getCurrentContainer();
727     NavigationService navService = (NavigationService) container.getComponentInstanceOfType(NavigationService.class);
728     try {
729       navService.destroyNavigation(new NavigationContext(nav.getKey(), new NavigationState(1)));
730     } catch (NavigationServiceException nex) {
731       LOG.warn("Failed to remove navigations", nex);
732     } catch (Exception e) {
733       LOG.warn("Failed to remove navigations", e);
734     } 
735   }
736 
737   /**
738    * Updates working work space
739    */
740   public static void updateWorkingWorkSpace() {
741     UIPortalApplication uiPortalApplication = Util.getUIPortalApplication();
742     UIWorkingWorkspace uiWorkingWS = uiPortalApplication.getChildById(UIPortalApplication.UI_WORKING_WS_ID);
743     PortalRequestContext pContext = Util.getPortalRequestContext();
744     pContext.addUIComponentToUpdateByAjax(uiWorkingWS);
745     pContext.setFullRender(true);
746   }
747 
748   /**
749    * Creates new group in /Spaces node and return groupId
750    *
751    * @param spaceName String
752    * @param creator   String
753    * @return groupId String
754    * @throws SpaceException
755    */
756   public static String createGroup(String spaceName, String creator) throws SpaceException {
757     return createGroup(spaceName, spaceName, creator);
758   }
759 
760   /**
761    * Creates new group in /Spaces node and return groupId
762    * 
763    * @param groupLabel Space Display name.
764    * @param spaceName Space name.
765    * @param creator Name of user who creating space.
766    * 
767    * @return groupId Id of created space group.
768    * @throws SpaceException
769    */
770   public static String createGroup(String groupLabel, String spaceName, String creator) throws SpaceException {
771     OrganizationService organizationService = getOrganizationService();
772     GroupHandler groupHandler = organizationService.getGroupHandler();
773     Group parentGroup;
774     Group newGroup;
775     String groupId;
776     String shortName;
777     try {
778       parentGroup = groupHandler.findGroupById(SPACE_GROUP);
779       // Creates new group
780       newGroup = groupHandler.createGroupInstance();
781       shortName = SpaceUtils.cleanString(spaceName);
782       groupId = parentGroup.getId() + "/" + shortName;
783       
784       PortalContainer portalContainer = PortalContainer.getInstance();
785       SpaceService spaceService = (SpaceService) portalContainer.getComponentInstanceOfType(SpaceService.class);
786       if (spaceService.getSpaceByGroupId(groupId) != null) {
787         shortName = buildGroupId(shortName, parentGroup.getId());
788         groupId = parentGroup.getId() + "/" + shortName;
789       }
790       
791       if (isSpaceNameExisted(spaceName)) {
792         throw new SpaceException(SpaceException.Code.SPACE_ALREADY_EXIST);
793       }
794       newGroup.setGroupName(shortName);
795       newGroup.setLabel(groupLabel);
796       newGroup.setDescription("the " + parentGroup.getId() + "/" + shortName + " group");
797       groupHandler.addChild(parentGroup, newGroup, true);
798     } catch (Exception e) {
799       if (e instanceof SpaceException) {
800         throw (SpaceException) e;
801       }
802       throw new SpaceException(SpaceException.Code.UNABLE_TO_CREATE_GROUP, e);
803     }
804 
805     try {
806       // adds user as creator (member, manager)
807       addCreatorToGroup(creator, groupId);
808     } catch (Exception e) {
809       // TODO:should rollback what has to be rollback here
810       throw new SpaceException(SpaceException.Code.UNABLE_TO_ADD_CREATOR, e);
811     }
812     return groupId;
813   }
814 
815   /**
816    * Removes a group owning a space
817    *
818    * @param space
819    * @throws SpaceException
820    */
821   public static void removeGroup(Space space) throws SpaceException {
822     try {
823       OrganizationService organizationService = getOrganizationService();
824       GroupHandler groupHandler = organizationService.getGroupHandler();
825       Group group = groupHandler.findGroupById(space.getGroupId());
826       groupHandler.removeGroup(group, true);
827     } catch (Exception e) {
828       throw new SpaceException(SpaceException.Code.UNABLE_TO_REMOVE_GROUP, e);
829     }
830   }
831 
832   /**
833    * Removes membership of users with a deleted spaces.
834    * 
835    * @param space
836    */
837   public static void removeMembershipFromGroup(Space space) {
838     if (space == null) return;
839     
840     // remove users from group with role is member
841     if (space.getMembers() != null) {
842       for (String userId : space.getMembers()) {
843         removeUserFromGroupWithMemberMembership(userId, space.getGroupId());
844       }
845     }
846     
847     // remove users from group with role is manager
848     if (space.getManagers() != null) {
849       for (String userId : space.getManagers()) {
850         removeUserFromGroupWithManagerMembership(userId, space.getGroupId());
851       }
852     }
853   }
854 
855   /**
856    * Checks if a space exists
857    *
858    * @param spaceName
859    * @return boolean if existed return true, else return false
860    * @throws SpaceException with code INTERNAL_SERVER_ERROR
861    */
862   public static boolean isSpaceNameExisted(String spaceName) throws SpaceException {
863     PortalContainer portalContainer = PortalContainer.getInstance();
864     SpaceService spaceService = (SpaceService) portalContainer.getComponentInstanceOfType(SpaceService.class);
865     // Checks whether spaceName has existed yet
866     if (spaceService.getSpaceByPrettyName(cleanString(spaceName)) != null) {
867       return true;
868     }
869     return false;
870   }
871 
872   /**
873    * When user chooses an existing group, that user will be added to that group as a manager
874    *
875    * @param creator String
876    * @param groupId String
877    */
878   public static void addCreatorToGroup(String creator, String groupId) {
879     addUserToGroupWithMemberMembership(creator, groupId);
880     addUserToGroupWithManagerMembership(creator, groupId);
881   }
882 
883   /**
884    * Adds the user to group with the membership (member, manager).
885    * 
886    * @param remoteId
887    * @param groupId
888    * @param membership
889    * @since 1.2.0-GA
890    */
891   private static void addUserToGroupWithMembership(String remoteId, String groupId, String membership) {
892     OrganizationService organizationService = getOrganizationService();
893     try {
894       // TODO: checks whether user is already manager?
895       MembershipHandler membershipHandler = organizationService.getMembershipHandler();
896       Membership found = membershipHandler.findMembershipByUserGroupAndType(remoteId, groupId, membership);
897       if (found == null && !MembershipTypeHandler.ANY_MEMBERSHIP_TYPE.equalsIgnoreCase(membership)) {
898         found = membershipHandler.findMembershipByUserGroupAndType(remoteId, groupId, MembershipTypeHandler.ANY_MEMBERSHIP_TYPE);
899       }
900       if (found != null) {
901         LOG.info("user: " + remoteId + " was already added to group: " + groupId + " with membership * or : " + membership);
902         return;
903       }
904       User user = organizationService.getUserHandler().findUserByName(remoteId);
905       MembershipType membershipType = organizationService.getMembershipTypeHandler().findMembershipType(membership);
906       GroupHandler groupHandler = organizationService.getGroupHandler();
907       Group existingGroup = groupHandler.findGroupById(groupId);
908       membershipHandler.linkMembership(user, existingGroup, membershipType, true);
909     } catch (Exception e) {
910       throw new RuntimeException("Unable to add user: " + remoteId + " to group: " + groupId + " with membership: " + membership, e);
911     }
912   }
913 
914   /**
915    * Adds the user to group with the membership (member). 
916    * 
917    * @param remoteId
918    * @param groupId
919    * @since 1.2.0-GA
920    */
921   public static void addUserToGroupWithMemberMembership(String remoteId, String groupId) {
922     addUserToGroupWithMembership(remoteId, groupId, MEMBER);
923   }
924   
925   /**
926    * Adds the user to group with the membership (manager). 
927    * 
928    * @param remoteId
929    * @param groupId
930    * @since 1.2.0-GA
931    */
932   public static void addUserToGroupWithManagerMembership(String remoteId, String groupId) {
933     addUserToGroupWithMembership(remoteId, groupId, getUserACL().getAdminMSType());
934   }
935   
936   /**
937    * Removes the user from group with the membership (member, manager). 
938    * 
939    * @param remoteId
940    * @param groupId
941    * @param membership
942    * @since 1.2.0-GA
943    */
944   private static void removeUserFromGroupWithMembership(String remoteId, String groupId, String membership) {
945     try {
946       OrganizationService organizationService = getOrganizationService();
947       MembershipHandler memberShipHandler = organizationService.getMembershipHandler();
948       if (MEMBER.equals(membership)) {
949           Collection<Membership> memberships = memberShipHandler.findMembershipsByUserAndGroup(remoteId, groupId);
950           if (memberships.size() == 0) {
951             LOG.info("User: " + remoteId + " is not a member of group: " + groupId);
952             return;
953           }
954           Iterator<Membership> itr = memberships.iterator();
955           while (itr.hasNext()) {
956             Membership mbShip = itr.next();
957             memberShipHandler.removeMembership(mbShip.getId(), true);
958           }
959       } else if (getUserACL().getAdminMSType().equals(membership)) {
960           Membership memberShip = memberShipHandler.findMembershipByUserGroupAndType(remoteId, groupId, getUserACL().getAdminMSType());
961           Membership any = memberShipHandler.findMembershipByUserGroupAndType(remoteId, groupId, MembershipTypeHandler.ANY_MEMBERSHIP_TYPE);
962           if (any != null) {
963             memberShipHandler.removeMembership(any.getId(), true);
964           }
965           if (memberShip == null) {
966             LOG.info("User: " + remoteId + " is not a manager of group: " + groupId);
967             return;
968           }
969           UserHandler userHandler = organizationService.getUserHandler();
970           User user = userHandler.findUserByName(remoteId);
971           memberShipHandler.removeMembership(memberShip.getId(), true);
972 
973 
974           MembershipType mbShipTypeMember = organizationService.getMembershipTypeHandler().findMembershipType(MEMBER);
975           GroupHandler groupHandler = organizationService.getGroupHandler();
976           memberShipHandler.linkMembership(user, groupHandler.findGroupById(groupId), mbShipTypeMember, true);
977       }
978     } catch (Exception e) {
979       LOG.warn("Failed to remove user: " + remoteId + " to group: " + groupId + " with membership: " + membership, e);
980     }
981   }
982   
983   /**
984    * Removes the user from group with member membership. 
985    * 
986    * @param remoteId
987    * @param groupId
988    * @since 1.2.0-GA
989    */
990   public static void removeUserFromGroupWithMemberMembership(String remoteId, String groupId) {
991     removeUserFromGroupWithMembership(remoteId, groupId, MEMBER);
992   }
993   
994   /**
995    * Removes the user from group with manager membership.
996    * 
997    * @param remoteId
998    * @param groupId
999    * @since 1.2.0-GA
1000    */
1001   public static void removeUserFromGroupWithManagerMembership(String remoteId, String groupId) {
1002     removeUserFromGroupWithMembership(remoteId, groupId, getUserACL().getAdminMSType());
1003   }
1004   
1005   /**
1006    * Creates group navigation if not existed or return existing group navigation based on groupId
1007    *
1008    * @param groupId String
1009    * @return spaceNav PageNavigation
1010    * @throws SpaceException
1011    */
1012   public static NavigationContext createGroupNavigation(String groupId) throws SpaceException {
1013     ExoContainer container = ExoContainerContext.getCurrentContainer();
1014     NavigationService navService = (NavigationService) container.getComponentInstance(NavigationService.class);
1015     //14-june-2011 Apply UserNavigation
1016     //PageNavigation spaceNav;
1017     NavigationContext navContext = navService.loadNavigation(SiteKey.group(groupId));
1018     try {
1019       if(navContext == null) {
1020         // creates new space navigation
1021         navContext = new NavigationContext(SiteKey.group(groupId), new NavigationState(1));
1022         navService.saveNavigation(navContext);
1023       }
1024       
1025       return navContext;
1026     } catch (Exception e) {
1027       // TODO:should rollback what has to be rollback here
1028       throw new SpaceException(SpaceException.Code.UNABLE_TO_CREAT_NAV, e);
1029     }
1030   }
1031 
1032   /**
1033    * Refreshes the current user portal (navigation caching refresh).
1034    *
1035    * @since 1.2.8
1036    */
1037   public static void refreshNavigation() {
1038     UserPortal userPortal = getUserPortal();
1039 
1040     if (userPortal != null) {
1041       userPortal.refresh();
1042     }
1043   }
1044 
1045   /**
1046    * Using this method to get the UserPortal make sure that the data is latest.
1047    * It's will remove the caching.
1048    * @return
1049    */
1050   public static UserPortal getUserPortal() {
1051     try {
1052       PortalRequestContext prc = Util.getPortalRequestContext();
1053       return prc.getUserPortalConfig().getUserPortal();
1054     } catch (Exception e) {
1055       //Makes sure that in the RestService still gets the UserPortal.
1056       try {
1057         return getUserPortalForRest();
1058       } catch (Exception e1) {
1059         return null;
1060       }
1061     }
1062   }
1063   
1064   /**
1065    * Get parent node of the current space.
1066    * 
1067    * @return
1068    * @throws Exception
1069    * @since 1.2.8
1070    */
1071   public static UserNode getParentNode() throws Exception {
1072     UIPortal uiPortal = Util.getUIPortal();
1073     UserPortal userPortal = getUserPortal();
1074     UserNode selectedNode = uiPortal.getSelectedUserNode();
1075     UserNode currParent = selectedNode.getParent();
1076     if (currParent != null) {
1077       try {
1078         userPortal.updateNode(currParent, Scope.CHILDREN, null);
1079       } catch (NavigationServiceException e) {
1080         currParent = null;
1081       }
1082     }
1083     return currParent;
1084   }
1085   
1086   /**
1087    * Gets the UserPortal when uses the RestService.
1088    * @return
1089    * @throws Exception
1090    */
1091   public static UserPortal getUserPortalForRest() throws Exception {
1092     return getUserPortalConfig().getUserPortal();
1093   }
1094 
1095   /**
1096    * Get user portal config.
1097    * 
1098    * @return
1099    * @throws Exception
1100    * @since 1.2.9
1101    */
1102   public static UserPortalConfig getUserPortalConfig() throws Exception {
1103     ExoContainer container = ExoContainerContext.getCurrentContainer();
1104     UserPortalConfigService userPortalConfigSer = (UserPortalConfigService)
1105                                                   container.getComponentInstanceOfType(UserPortalConfigService.class);
1106 
1107     UserPortalContext NULL_CONTEXT = new UserPortalContext() {
1108       public ResourceBundle getBundle(UserNavigation navigation) {
1109         return null;
1110       }
1111 
1112       public Locale getUserLocale() {
1113         return Locale.ENGLISH;
1114       }
1115     };
1116     
1117     String remoteId = ConversationState.getCurrent().getIdentity().getUserId();
1118     UserPortalConfig userPortalCfg = userPortalConfigSer.
1119                                      getUserPortalConfig(userPortalConfigSer.getDefaultPortal(), remoteId, NULL_CONTEXT);
1120     return userPortalCfg;
1121   }
1122   
1123   /**
1124    * Removes group navigations.
1125    *
1126    * @param groupId
1127    * @throws SpaceException
1128    */
1129   public static void removeGroupNavigation(String groupId) throws SpaceException {
1130     ExoContainer container = ExoContainerContext.getCurrentContainer();
1131     NavigationService navService = (NavigationService) container.getComponentInstanceOfType(NavigationService.class);
1132     try {
1133       NavigationContext nav = navService.loadNavigation(SiteKey.group(groupId));
1134       if (nav != null) {
1135         navService.destroyNavigation(nav);
1136       } else {
1137         throw new Exception("spaceNav is null");
1138       }
1139     } catch (Exception e) {
1140       throw new SpaceException(SpaceException.Code.UNABLE_TO_REMOVE_NAV, e);
1141     }
1142 
1143   }
1144 
1145   /**
1146    * Gets NavigationContext by a space's groupId
1147    *
1148    * @param groupId
1149    * @throws Exception
1150    */
1151   public static NavigationContext getGroupNavigationContext(String groupId) throws Exception {
1152     ExoContainer container = ExoContainerContext.getCurrentContainer();
1153     NavigationService navService = (NavigationService) container.getComponentInstance(NavigationService.class);
1154     return navService.loadNavigation(SiteKey.group(groupId));
1155   }
1156   
1157   /**
1158   * Gets userNavigation by a space's groupId
1159   *
1160   * @param groupId
1161   * @throws Exception
1162   */
1163     public static UserNavigation getGroupNavigation(String groupId) throws Exception {
1164       UserPortal userPortal = getUserPortal();
1165       if (userPortal != null) {
1166         return getUserPortal().getNavigation(SiteKey.group(groupId));
1167       }
1168       return null;
1169     }
1170 
1171   /**
1172    * This related to a bug from portal. When this bug is resolved, use userNavigation.getNode(space.getUrl());
1173    *
1174    * @param userNavigation
1175    * @param spaceUrl
1176    * @return
1177    */
1178   public static UserNode getHomeNode(UserNavigation userNavigation, String spaceUrl) {
1179     //Need to get usernode base on resolvePath
1180     return getUserPortal().resolvePath(userNavigation, null, spaceUrl);
1181   }
1182   
1183   /**
1184    * Retrieving the UserNode base on the UserNavigation
1185    *
1186    * @param userNavigation
1187    * @return
1188    */
1189   public static UserNode getHomeNode(UserNavigation userNavigation) {
1190     return getUserPortal().getNode(userNavigation, Scope.SINGLE, null, null);
1191   }
1192   
1193  
1194   /**
1195    * 
1196    * @param spaceNavCtx
1197    * @param spaceUrl
1198    * @return
1199    */
1200   public static NodeContext<NodeContext<?>> getHomeNodeWithChildren(NavigationContext spaceNavCtx, String spaceUrl) {
1201     //Need to get usernode base on resolvePath
1202     ExoContainer container = ExoContainerContext.getCurrentContainer();
1203     NavigationService navService = (NavigationService) container.getComponentInstance(NavigationService.class);
1204     return NavigationUtils.loadNode(navService, spaceNavCtx, spaceUrl);  
1205   }
1206   
1207   /**
1208    * Retrieving the UserNode with Children base on the spaceUrl and UserNavigation.
1209    * When user can use this method to get homeNode, you can not call the update node
1210    *  to getChildren()
1211    *  
1212    * @param userNavigation
1213    * @param spaceUrl
1214    * @return
1215    */
1216   public static UserNode getHomeNodeWithChildren(UserNavigation userNavigation, String spaceUrl) {
1217     //Need to get usernode base on resolvePath
1218     UserNode homeNode = getUserPortal().resolvePath(userNavigation, null, spaceUrl);
1219     getUserPortal().updateNode(homeNode, Scope.CHILDREN, null);
1220     return homeNode;
1221     
1222   }
1223   
1224   /**
1225    * Retrieving the UserNode of Space when is given Space instance
1226    *  
1227    * @param space space
1228    * @return
1229    */
1230   public static UserNode getSpaceUserNode(Space space) throws Exception {
1231     return getSpaceUserNode(space, null);
1232   }
1233 
1234   public static UserNode getSpaceUserNode(Space space, UserNodeFilterConfig filter) throws Exception {
1235     NavigationContext spaceNavCtx = getGroupNavigationContext(space.getGroupId());
1236 
1237     UserNavigation userNav = SpaceUtils.getUserPortal().getNavigation(spaceNavCtx.getKey());
1238 
1239     UserNode parentUserNode = SpaceUtils.getUserPortal().getNode(userNav, Scope.CHILDREN, filter, null);
1240     UserNode spaceUserNode = parentUserNode.getChildrenSize() > 0 ? parentUserNode.getChild(0) : null;
1241 
1242     if (spaceUserNode != null) {
1243       SpaceUtils.getUserPortal().updateNode(spaceUserNode, Scope.CHILDREN, null);
1244     } else {
1245       LOG.warn("Failed to get because of spaceUserNode is NULL");
1246     }
1247 
1248     return spaceUserNode;
1249   }
1250 
1251   public static List<UserNode> getSpaceUserNodeChildren(Space space) throws Exception {
1252     return new ArrayList<UserNode>(getSpaceUserNode(space).getChildren());
1253   }
1254 
1255 
1256 
1257   /**
1258    * Sorts spaces list by priority and alphabet order
1259    *
1260    * @param spaces
1261    * @return ordered spaces list
1262    */
1263   public static List<Space> getOrderedSpaces(List<Space> spaces) {
1264     Iterator<Space> itr = spaces.iterator();
1265     List<Space> orderedSpaces = new ArrayList<Space>();
1266     List<Space> middleSpaces = new ArrayList<Space>();
1267     List<Space> lowSpaces = new ArrayList<Space>();
1268     Space space = null;
1269     while (itr.hasNext()) {
1270       space = itr.next();
1271       String priority = space.getPriority();
1272       if (priority.equals(Space.HIGH_PRIORITY)) {
1273         orderedSpaces.add(space);
1274       } else if (priority.equals(Space.INTERMEDIATE_PRIORITY)) {
1275         middleSpaces.add(space);
1276       } else if (priority.equals(Space.LOW_PRIORITY)) {
1277         lowSpaces.add(space);
1278       }
1279     }
1280     for (Space sp : middleSpaces) {
1281       orderedSpaces.add(sp);
1282     }
1283     for (Space sp : lowSpaces) {
1284       orderedSpaces.add(sp);
1285     }
1286     return orderedSpaces;
1287   }
1288 
1289   /**
1290    * Utility for counting the number of members in a space
1291    *
1292    * @param space
1293    * @return the number of members
1294    * @throws SpaceException
1295    */
1296   public static int countMembers(Space space) throws SpaceException {
1297     if (space.getMembers() != null) {
1298       return space.getMembers().length;
1299     }
1300     return 0;
1301   }
1302 
1303   /**
1304    * Gets app status in a space
1305    *
1306    * @param space
1307    * @param appId
1308    * @return appStatus
1309    */
1310   public static String getAppStatus(Space space, String appId) {
1311     String installedApps = space.getApp();
1312     if (installedApps == null) {
1313       return null;
1314     }
1315     if (installedApps.contains(appId)) {
1316       String appStatusPattern = getAppStatusPattern(installedApps, appId);
1317       return appStatusPattern.split(":")[3];
1318     }
1319     return null;
1320   }
1321 
1322   /**
1323    * Gets appNodeName in a space by its appId
1324    *
1325    * @param space
1326    * @param appId
1327    * @return
1328    */
1329   public static String getAppNodeName(Space space, String appId) {
1330     String installedApps = space.getApp();
1331     if (installedApps == null) {
1332       return null;
1333     }
1334     if (installedApps.contains(appId)) {
1335       String appStatusPatern = getAppStatusPattern(installedApps, appId);
1336       return appStatusPatern.split(":")[1];
1337     }
1338     return null;
1339   }
1340 
1341   /**
1342    * Checks if an application is removable or not. Default will return true.
1343    *
1344    * @param space
1345    * @param appId
1346    */
1347   public static boolean isRemovableApp(Space space, String appId) {
1348     String installedApps = space.getApp();
1349     if (installedApps.contains(appId)) {
1350       String appStatus = getAppStatusPattern(installedApps, appId);
1351       String[] spliter = appStatus.split(":");
1352       if (spliter[2].equals("false")) {
1353         return false;
1354       }
1355     }
1356     return true;
1357   }
1358 
1359   /**
1360    * Gets all application id from a space
1361    *
1362    * @param space
1363    * @return
1364    */
1365   public static List<String> getAppIdList(Space space) {
1366     List<String> appIdList = new ArrayList<String>();
1367     String installedApps = space.getApp();
1368     if (installedApps != null) {
1369       if (installedApps.contains(",")) {
1370         String[] appStatuses = installedApps.split(",");
1371         for (String appStatus : appStatuses) {
1372           appIdList.add((appStatus.split(":"))[0]);
1373         }
1374       } else {
1375         appIdList.add((installedApps.split(":"))[0]);
1376       }
1377     }
1378     return appIdList;
1379   }
1380 
1381   /**
1382    * Utility for getting absolute url
1383    *
1384    * @return absolute url
1385    * @throws SpaceException
1386    */
1387   public static String getAbsoluteUrl() throws SpaceException {
1388     PortalRequestContext portalRequestContext = Util.getPortalRequestContext();
1389     HttpServletRequest request = portalRequestContext.getRequest();
1390     String str = request.getRequestURL().toString();
1391     return str.substring(0, str.indexOf(portalRequestContext.getRequestContextPath()));
1392   }
1393 
1394   /**
1395    * Checks if a user is removed or not.<br>
1396    *
1397    * @param userName User name for checking.
1398    * @throws SpaceException if user is removed.
1399    */
1400   public static void checkUserExisting(String userName) throws SpaceException {
1401     OrganizationService orgService = getOrganizationService();
1402     User user = null;
1403     try {
1404       user = orgService.getUserHandler().findUserByName(userName);
1405     } catch (Exception e) {
1406       throw new SpaceException(SpaceException.Code.ERROR_RETRIEVING_USER, e);
1407     }
1408 
1409     if (user == null) {
1410       throw new SpaceException(SpaceException.Code.ERROR_RETRIEVING_USER);
1411     }
1412   }
1413 
1414   /**
1415    * Check an application is installed or not yet.
1416    *
1417    * @param space
1418    * @param appId
1419    * @return
1420    */
1421   public static boolean isInstalledApp(Space space, String appId) {
1422     String installedApps = space.getApp();
1423     if (installedApps == null) {
1424       return false;
1425     }
1426     String[] apps = installedApps.split(",");
1427     String[] appPart;
1428     for (int idx = 0; idx < apps.length; idx++) {
1429       appPart = apps[idx].split(":");
1430       if (appPart[0].equals(appId) || (appPart.length > 1 && appPart[1].equals(appId))) {
1431         return true;
1432       }
1433     }
1434     return false;
1435   }
1436 
1437   /**
1438    * Get display application name in formal.
1439    *
1440    * @param appDisplayName
1441    * @return
1442    */
1443   public static String getDisplayAppName(String appDisplayName) {
1444     int length = appDisplayName.length();
1445     if (appDisplayName.toLowerCase().endsWith("portlet")) {
1446       return appDisplayName.substring(0, length - 7).trim();
1447     }
1448     if (appDisplayName.toLowerCase().endsWith("gadget")) {
1449       return appDisplayName.substring(0, length - 6).trim();
1450     }
1451     return appDisplayName;
1452   }
1453 
1454   /**
1455    * Gets appStatusPattern: [appId:appNodeName:isRemovableString:status]
1456    *
1457    * @param installedApps
1458    * @param appId
1459    * @return
1460    */
1461   private static String getAppStatusPattern(String installedApps, String appId) {
1462     if (installedApps == null) {
1463       return null;
1464     }
1465     if (installedApps.contains(appId)) {
1466       String[] apps = installedApps.split(",");
1467       for (String app : apps) {
1468         if (app.contains(appId)) {
1469           String[] splited = app.split(":");
1470           if (splited.length != 4) {
1471             LOG.warn("appStatus is not in correct form of [appId:appNodeName:isRemovableString:status] : "
1472                     + app);
1473             return null;
1474           }
1475 
1476           return app;
1477         }
1478       }
1479     }
1480     return null;
1481   }
1482 
1483   /**
1484    * Checks if a group can have access to an application
1485    *
1486    * @param app
1487    * @param groupId
1488    * @return
1489    * @throws Exception
1490    */
1491   private static boolean hasAccessPermission(Application app, String groupId) throws Exception {
1492     List<String> permissions = app.getAccessPermissions();
1493     if (permissions == null) {
1494       return false;
1495     }
1496     for (String ele : permissions) {
1497       if (hasViewPermission(ele, groupId)) {
1498         return true;
1499       }
1500     }
1501     return false;
1502   }
1503 
1504   /**
1505    * Gets Organization Service
1506    *
1507    * @return
1508    */
1509   public static OrganizationService getOrganizationService() {
1510     PortalContainer portalContainer = PortalContainer.getInstance();
1511     return (OrganizationService) portalContainer.getComponentInstanceOfType(OrganizationService.class);
1512   }
1513 
1514   /**
1515    * Gets dataStorage
1516    *
1517    * @return
1518    */
1519   public static DataStorage getDataStorage() {
1520     PortalContainer portalContainer = PortalContainer.getInstance();
1521     return (DataStorage) portalContainer.getComponentInstanceOfType(DataStorage.class);
1522   }
1523 
1524   /**
1525    * Checks if a group have access permission to an application category
1526    *
1527    * @param app
1528    * @param groupId
1529    * @return true if that group has access permission; otherwise, false
1530    * @throws Exception
1531    */
1532   private static boolean hasAccessPermission(ApplicationCategory app, String groupId) throws Exception {
1533     List<String> permissions = app.getAccessPermissions();
1534     if (permissions == null) {
1535       return false;
1536     }
1537     for (String ele : permissions) {
1538       if (hasViewPermission(ele, groupId)) {
1539         return true;
1540       }
1541     }
1542     return false;
1543   }
1544 
1545   /**
1546    * Checks view permission
1547    *
1548    * @param expPerm
1549    * @param groupId
1550    * @return
1551    * @throws Exception
1552    */
1553   private static boolean hasViewPermission(String expPerm, String groupId) throws Exception {
1554     if (UserACL.EVERYONE.equals(expPerm)) {
1555       return true;
1556     }
1557     String[] temp = expPerm.split(":");
1558     if (temp.length < 2) {
1559       return false;
1560     }
1561     String tempExp = temp[1].trim();
1562     if (tempExp.equals(groupId) || tempExp.equals(PLATFORM_USERS_GROUP)) {
1563       return true;
1564     }
1565     return false;
1566   }
1567 
1568   /**
1569    * Gets localized string value
1570    *
1571    * @param localizedString
1572    * @param portletName
1573    * @return
1574    */
1575   private static String getLocalizedStringValue(LocalizedString localizedString, String portletName) {
1576     if (localizedString == null || localizedString.getDefaultString() == null) {
1577       return portletName;
1578     } else {
1579       return localizedString.getDefaultString();
1580     }
1581   }
1582 
1583   /**
1584    * Gets application registry service
1585    *
1586    * @return
1587    */
1588   private static ApplicationRegistryService getApplicationRegistryService() {
1589     PortalContainer portalContainer = PortalContainer.getInstance();
1590     return (ApplicationRegistryService) portalContainer.getComponentInstanceOfType(ApplicationRegistryService.class);
1591   }
1592   
1593   /**
1594    * Filter all invalid character (anything except word, number, space and search wildcard) from Space search conditional.
1595    * @since: 1.2.2
1596    * @param input String
1597    * @return
1598    */
1599 
1600   public static String removeSpecialCharacterInSpaceFilter(String input){
1601     //We don't remove the character "'" because it's a normal character in french 
1602     String result = input.replaceAll("[^\\pL\\pM\\p{Nd}\\p{Nl}\\p{Pc}[\\p{InEnclosedAlphanumerics}&&\\p{So}]\\?\\*%0-9\\']", " ");
1603     result = result.replaceAll("\\s+", " ");
1604     return result.trim();
1605   }
1606   
1607   /**
1608    * As the similarity is provided in the search term, we need to extract the keyword that user enter in 
1609    * the search form
1610    * 
1611    * @param input the search value include the similarity
1612    * @return the search condition after process
1613    */
1614   public static String processUnifiedSearchCondition(String input) {
1615     if (StringUtils.isEmpty(input)) {
1616       return input;
1617     } else if (input.indexOf("~") < 0 || input.indexOf("\\~") > 0) {
1618       return input.trim();
1619     }
1620     StringBuilder builder = new StringBuilder();
1621     //The similarity is added for each word in the search condition, ex : space~0.5 test~0.5
1622     //then we need to process each word separately 
1623     String[] tab = input.split(" ");
1624     for (String s : tab){
1625       if (s.isEmpty()) continue;
1626       if (s.indexOf("~") > -1) {
1627         String searchTerm = s.substring(0, s.lastIndexOf("~"));
1628         builder.append(searchTerm).append(" ");
1629       } else {
1630         builder.append(s).append(" ");
1631       }
1632       
1633     }
1634     return builder.toString().trim();
1635   }
1636 
1637   /**
1638    * Builds pretty name base on the basic name in case create more than one space with the same name.
1639    * 
1640    * @param prettyName
1641    * @param parentGroupId
1642    * @return
1643    * @since 1.2.8
1644    */
1645   public static String buildGroupId(String prettyName, String parentGroupId) {
1646     String checkedGroupId = prettyName;
1647     String mainPatternGroupId = null;
1648     String numberPattern = NUMBER_REG_PATTERN;
1649     if (checkedGroupId.substring(checkedGroupId.lastIndexOf(UNDER_SCORE_STR) + 1).matches(numberPattern)) {
1650       mainPatternGroupId = checkedGroupId.substring(0, checkedGroupId.lastIndexOf(UNDER_SCORE_STR));
1651     } else {
1652       mainPatternGroupId = checkedGroupId;
1653     }
1654     
1655     boolean hasNext = true;
1656     int extendPattern = 0;
1657     
1658     while (hasNext) {
1659       ++extendPattern;
1660       checkedGroupId = cleanString(mainPatternGroupId + SPACE_STR + extendPattern);
1661       ExoContainer container = ExoContainerContext.getCurrentContainer();
1662       
1663       SpaceService spaceService = (SpaceService) container.getComponentInstanceOfType(SpaceService.class);
1664       if (spaceService.getSpaceByGroupId(parentGroupId + "/" + checkedGroupId) != null) {
1665         continue;
1666       }
1667       
1668       IdentityManager idm = (IdentityManager) container.getComponentInstanceOfType(IdentityManager.class);
1669       Identity identity = idm.getOrCreateIdentity(SpaceIdentityProvider.NAME, checkedGroupId, true);
1670       if (identity == null) {
1671         hasNext = false;
1672       }
1673     }
1674     
1675     return checkedGroupId;
1676   }
1677   
1678   /**
1679    * Builds pretty name base on the basic name in case create more than one space with the same name.
1680    * 
1681    * @param space
1682    * @return
1683    */
1684   public static String buildPrettyName(Space space) {
1685     String checkedPrettyName = space.getPrettyName();
1686     String mainPatternPrettyName = null;
1687     String numberPattern = NUMBER_REG_PATTERN;
1688     if (checkedPrettyName.substring(checkedPrettyName.lastIndexOf(UNDER_SCORE_STR) + 1).matches(numberPattern)) {
1689       mainPatternPrettyName = checkedPrettyName.substring(0, checkedPrettyName.lastIndexOf(UNDER_SCORE_STR));
1690     } else {
1691       mainPatternPrettyName = checkedPrettyName;
1692     }
1693     
1694     boolean hasNext = true;
1695     int extendPattern = 0;
1696     
1697     while (hasNext) {
1698       ++extendPattern;
1699       checkedPrettyName = cleanString(mainPatternPrettyName + SPACE_STR + extendPattern);
1700       ExoContainer container = ExoContainerContext.getCurrentContainer();
1701       IdentityManager idm = (IdentityManager) container.getComponentInstanceOfType(IdentityManager.class);
1702       Identity identity = idm.getOrCreateIdentity(SpaceIdentityProvider.NAME, checkedPrettyName, true);
1703       if (identity == null) {
1704         hasNext = false;
1705       }
1706     }
1707     
1708     return checkedPrettyName;
1709   }
1710   
1711   /**
1712    * Gets the UserACL which helps to get Membership role to avoid hard code.
1713    * @return UserACL object
1714    */
1715   public static UserACL getUserACL() {
1716     ExoContainer container = ExoContainerContext.getCurrentContainer();
1717     return (UserACL) container.getComponentInstanceOfType(UserACL.class);
1718   }
1719   
1720   /**
1721    * Checks if an specific user has membership in group group with input membership type.
1722    * 
1723    * @param remoteId User to be checked.
1724    * @param groupId Group information.
1725    * @param membershipType input membership type to check.
1726    * @return true if user has membership in group with input type.
1727    */
1728   public static boolean isUserHasMembershipTypesInGroup(String remoteId, String groupId, String membershipType) {
1729 	  if (remoteId == null || groupId == null || membershipType == null) return false;
1730 
1731     ConversationState conversationState = ConversationState.getCurrent();
1732     if (conversationState != null && conversationState.getIdentity() != null
1733         && remoteId.equals(conversationState.getIdentity().getUserId())) {
1734       Permission permission = new Permission();
1735       permission.setGroupId(groupId);
1736       permission.setMembership(membershipType);
1737       return getUserACL().hasPermission(conversationState.getIdentity(), permission.getValue());
1738     }
1739 
1740     try {
1741       Collection<Membership> membershipsList = getOrganizationService()
1742         .getMembershipHandler().findMembershipsByUserAndGroup(remoteId, groupId);
1743       
1744       for (Membership membership : membershipsList) {
1745         if (membershipType.equals(membership.getMembershipType())) 
1746           return true;
1747       }
1748     } catch (Exception e) {
1749       return false;
1750     }
1751     return false;
1752   }
1753   
1754   /**
1755    * Get users that have membership with group by input membership types.
1756    * 
1757    * @param groupId Group information.
1758    * @param membershipTypes membership types to be get.
1759    * @return List of users that have membership with group is input membership type.
1760    */
1761   public static List<String> findMembershipUsersByGroupAndTypes(String groupId, String... membershipTypes) {
1762     if (groupId == null || membershipTypes == null) return Collections.<String>emptyList();
1763     
1764     Set<String> userNames = new HashSet<String>();
1765     try {
1766       Group group = getOrganizationService().getGroupHandler().findGroupById(groupId);
1767       ListAccess<Membership> membershipsListAccess = getOrganizationService()
1768           .getMembershipHandler().findAllMembershipsByGroup(group);
1769       
1770       Membership[] memberships = membershipsListAccess.load(0, membershipsListAccess.getSize());
1771       
1772       List<String> types = Arrays.asList(membershipTypes);
1773       
1774       for (Membership membership : memberships) {
1775         if (!types.contains(membership.getMembershipType())) continue;
1776         
1777         String userName = membership.getUserName();
1778         User user = getOrganizationService().getUserHandler()
1779             .findUserByName(userName, UserStatus.ENABLED);
1780         
1781         if (user != null) {
1782           userNames.add(userName);
1783         }
1784       }
1785     } catch (Exception e) {
1786       LOG.warn("Failed to get space members, groupId = " + groupId + " and roles = " + String.join(",",membershipTypes), e);
1787       return new ArrayList<String>();
1788     }
1789     
1790     return new ArrayList<String>(userNames);
1791   }
1792 }