/*
* Copyright (C) 2003-2007 eXo Platform SAS.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see<http://www.gnu.org/licenses/>.
*/
package org.exoplatform.social.core.space.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.exoplatform.application.registry.Application;
import org.exoplatform.application.registry.ApplicationCategory;
import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.container.PortalContainer;
import org.exoplatform.portal.config.DataStorage;
import org.exoplatform.portal.config.UserACL;
import org.exoplatform.portal.config.UserPortalConfigService;
import org.exoplatform.portal.config.model.ApplicationState;
import org.exoplatform.portal.config.model.ApplicationType;
import org.exoplatform.portal.config.model.Container;
import org.exoplatform.portal.config.model.ModelObject;
import org.exoplatform.portal.config.model.Page;
import org.exoplatform.portal.config.model.PortalConfig;
import org.exoplatform.portal.config.model.TransientApplicationState;
import org.exoplatform.portal.mop.navigation.NavigationServiceException;
import org.exoplatform.portal.mop.navigation.Scope;
import org.exoplatform.portal.mop.user.UserNavigation;
import org.exoplatform.portal.mop.user.UserNode;
import org.exoplatform.portal.mop.user.UserPortal;
import org.exoplatform.portal.pom.config.Utils;
import org.exoplatform.portal.pom.spi.gadget.Gadget;
import org.exoplatform.portal.pom.spi.portlet.Portlet;
import org.exoplatform.portal.pom.spi.portlet.PortletBuilder;
import org.exoplatform.portal.webui.portal.UIPortal;
import org.exoplatform.portal.webui.util.Util;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.social.core.space.SpaceApplicationConfigPlugin;
import org.exoplatform.social.core.space.SpaceApplicationConfigPlugin.SpaceApplication;
import org.exoplatform.social.core.space.SpaceException;
import org.exoplatform.social.core.space.SpaceUtils;
import org.exoplatform.social.core.space.model.Space;
import org.exoplatform.social.core.space.spi.SpaceApplicationHandler;
import org.exoplatform.social.core.space.spi.SpaceService;
/**
* Default implementation for working with space applications.
*
* @author <a href="mailto:tungcnw@gmail.com">dang.tung</a>
* @author <a href="http://hoatle.net">hoatle (hoatlevan at gmail dot com)</a>
* @since OCt 17, 2008
*/
public class DefaultSpaceApplicationHandler implements SpaceApplicationHandler {
private static final Log LOG = ExoLogger.getLogger(DefaultSpaceApplicationHandler.class);
public static final String NAME = "classic";
public static final String SPACE_TEMPLATE_PAGE_ID = "portal::classic::spacetemplate";
public static final String APPLICATION_CONTAINER = "Application";
private static final String SPACE_DEFAULT_ICON = "SpaceDefaultIcon";
/**
* The {groupId} preference value pattern
*
* @since 1.2.0-GA
*/
private static final String GROUP_ID_PREFERENCE = "{groupId}";
/**
* The {modifiedGroupId} preference value pattern
*
* @since 1.2.0-GA
*/
private static final String MODIFIED_GROUP_ID_PREFERENCE = "{modifiedGroupId}";
/**
* The {pageName} preference value pattern
*
* @since 1.2.0-GA
*/
private static final String PAGE_NAME_PREFERENCE = "{pageName}";
/**
* The {pageUrl} preference value pattern
*
* @since 1.2.0-GA
*/
private static final String PAGE_URL_PREFERENCE = "{pageUrl}";
/**
* The {spaceUrl} preference value pattern
*
* @since 1.2.0-GA
*/
private static final String SPACE_URL_PREFERENCE = "{spaceUrl}";
private PortalContainer container = PortalContainer.getInstance();
private DataStorage dataStorage = null;
private SpaceService spaceService;
private static Map<ApplicationCategory, List<Application>> appStoreCache = null;
private static List<Application> appCache = new ArrayList<Application>();
/**
* Constructor.
*
* @param dataStorage
*/
public DefaultSpaceApplicationHandler(DataStorage dataStorage) {
this.dataStorage = dataStorage;
}
/**
* {@inheritDoc}
*/
public void initApps(Space space, SpaceApplicationConfigPlugin spaceApplicationConfigPlugin) throws SpaceException {
try {
UserNavigation spaceNav = SpaceUtils.createGroupNavigation(space.getGroupId());
UserPortal userPortal = SpaceUtils.getUserPortal();
UserNode parentNode = userPortal.getNode(spaceNav, Scope.CHILDREN, null, null);
UserNode homeNode = createPageNodeFromApplication(parentNode, space, spaceApplicationConfigPlugin.getHomeApplication(), null, true);
SpaceService spaceService = getSpaceService();
userPortal.updateNode(parentNode, Scope.CHILDREN, null);
for (SpaceApplication spaceApplication : spaceApplicationConfigPlugin.getSpaceApplicationList()) {
UserNode appNode = createPageNodeFromApplication(homeNode, space, spaceApplication, null, false);
spaceService.installApplication(space, spaceApplication.getPortletName());
}
//commit the parentNode to JCR
userPortal.saveNode(parentNode, null);
} catch (Exception e) {
throw new SpaceException(SpaceException.Code.UNABLE_TO_INIT_APP, e);
}
}
/**
* {@inheritDoc}
*/
public void deInitApp(Space space) throws SpaceException {
try {
String groupId = space.getGroupId();
UserNavigation spaceNav = SpaceUtils.getGroupNavigation(groupId);
// return in case group navigation was removed by portal SOC-548
if (spaceNav == null) {
return;
}
UserNode userNode = SpaceUtils.getHomeNodeWithChildren(spaceNav, groupId);
Collection<UserNode> spaceNodes = userNode.getChildren();
for (UserNode spaceNode : spaceNodes) {
String pageId = spaceNode.getPageRef();
Page page = dataStorage.getPage(pageId);
dataStorage.remove(page);
}
SpaceUtils.removeGroupNavigation(groupId);
} catch (Exception e) {
throw new SpaceException(SpaceException.Code.UNABLE_TO_DEINIT_APP, e);
}
}
/**
* {@inheritDoc}
*/
public void activateApplication(Space space, String appId, String appName) throws SpaceException {
activateApplicationClassic(space, appId, appName);
}
/**
* {@inheritDoc}
*/
public void deactiveApplication(Space space, String appId) throws SpaceException {
deactivateApplicationClassic(space, appId);
}
/**
* {@inheritDoc}
*/
public void installApplication(Space space, String appId) throws SpaceException {
}
/**
* {@inheritDoc}
*/
public void removeApplication(Space space, String appId, String appName) throws SpaceException {
removeApplicationClassic(space, appId, appName);
}
/**
* {@inheritDoc}
*/
public void removeApplications(Space space) throws SpaceException {
try {
String[] apps = space.getApp().split(",");
String[] appPart = null;
for (int i = 0; i < apps.length; i++) {
appPart = apps[i].split(":");
removeApplication(space, appPart[0], appPart[1]);
}
} catch (Exception e) {
throw new SpaceException(SpaceException.Code.UNABLE_TO_REMOVE_APPLICATIONS, e);
}
}
/**
* {@inheritDoc}
*/
public String getName() {
return NAME;
}
/**
* Activates classic application type in a space
*
* @param space
* @param appId
* @throws SpaceException
*/
private void activateApplicationClassic(Space space, String appId, String appName) throws SpaceException {
UserNavigation nav = SpaceUtils.createGroupNavigation(space.getGroupId());
SpaceApplication spaceApplication = new SpaceApplication();
spaceApplication.setPortletName(appId);
UserNode parentNode = SpaceUtils.getHomeNode(nav, space.getUrl());
UserNode pageNode = createPageNodeFromApplication(parentNode, space, spaceApplication, appName, false);
SpaceUtils.getUserPortal().saveNode(parentNode, null);
}
/**
* Deactivates an application in a space
*
* @param space
* @param appId
*/
private void deactivateApplicationClassic(Space space, String appId) {
}
/**
* Removes an classic-type application from a space
*
* @param space
* @param appId
* @throws SpaceException
*/
private void removeApplicationClassic(Space space, String appId, String appName) throws SpaceException {
try {
String groupId = space.getGroupId();
UserNavigation nav = SpaceUtils.getGroupNavigation(groupId);
UserNode homeNode = SpaceUtils.getHomeNodeWithChildren(nav, space.getUrl());
String nodeName = appName;
ExoContainer container = ExoContainerContext.getCurrentContainer();
UserPortalConfigService configService = (UserPortalConfigService) container.getComponentInstanceOfType(UserPortalConfigService.class);
UserNode removedNode = null;
removedNode = homeNode.getChild(nodeName);
if (removedNode == null) {
removedNode = homeNode.getChild(nodeName.toLowerCase());
}
if (removedNode != null) {
homeNode.removeChild(removedNode.getName());
}
//remove page
if (removedNode != null) {
String pageRef = removedNode.getPageRef();
if (pageRef != null && pageRef.length() > 0) {
Page page = configService.getPage(pageRef);
if (page != null)
dataStorage.remove(page);
UIPortal uiPortal = Util.getUIPortal();
// Remove from cache
uiPortal.setUIPage(pageRef, null);
}
}
getUserPortal().saveNode(homeNode, null);
} catch (Exception e) {
throw new SpaceException(SpaceException.Code.UNABLE_TO_REMOVE_APPLICATION, e);
}
}
/**
* Gets an application from a list
*
* @param apps
* @param appId
* @return
*/
private Application getApplication(List<Application> apps, String appId) {
for (Application app : apps) {
if (app.getApplicationName().equals(appId)) {
return app;
}
}
return null;
}
/**
* Creates page node from application. - Creates Application instance from appId. <br /> - Creates Page instance and set
* the newly-created application for that page; adds application to container. <br /> - Creates PageNode instance and
* returns that pageNode.
*
* @param space
* @param spaceApplication
* @param isRoot
* @return
* @since 1.2.0-GA
*/
private UserNode createPageNodeFromApplication(UserNode parentNode, Space space,
SpaceApplication spaceApplication,
String appName,
boolean isRoot) throws SpaceException {
String appId = spaceApplication.getPortletName();
Application app = getApplication(space, appId);
String contentId = app.getContentId();
if (contentId == null) {
contentId = app.getCategoryName() + "/" + app.getApplicationName();
}
String appInstanceId = PortalConfig.GROUP_TYPE + "#" + space.getGroupId() + ":/" + contentId
+ "/" + app.getApplicationName() + System.currentTimeMillis();
org.exoplatform.portal.config.model.Application<Gadget> gadgetApplication = null;
org.exoplatform.portal.config.model.Application<Portlet> portletApplication = null;
if (app.getType() == ApplicationType.GADGET) {
TransientApplicationState<Gadget> gadgetState = new TransientApplicationState<Gadget>(app.getApplicationName());
gadgetApplication = org.exoplatform.portal.config.model.Application.createGadgetApplication();
gadgetApplication.setState(gadgetState);
gadgetApplication.setAccessPermissions(new String[]{"*:" + space.getGroupId()});
gadgetApplication.setShowInfoBar(false);
} else {
portletApplication = createPortletApplication(appInstanceId, space, isRoot);
portletApplication.setAccessPermissions(new String[]{"*:" + space.getGroupId()});
portletApplication.setShowInfoBar(false);
}
String pageTitle = space.getDisplayName() + " - " + app.getDisplayName();
String pageName = app.getApplicationName();
//is the application installed?
if (SpaceUtils.isInstalledApp(space, appId) && (appName != null)) {
pageName = appName;
}
UserPortalConfigService userPortalConfigService = getUserPortalConfigService();
Page page = null;
try {
page = userPortalConfigService.createPageTemplate("space",
PortalConfig.GROUP_TYPE,
space.getGroupId());
page.setName(pageName);
page.setTitle(pageTitle);
dataStorage.create(page);
page = dataStorage.getPage(page.getPageId());
//setting some data to page.
setPage(space, app, gadgetApplication, portletApplication, page);
dataStorage.save(page);
} catch (Exception e) {
LOG.warn(e.getMessage(), e);
}
String label = SpaceUtils.getDisplayAppName(app.getDisplayName());
if (spaceApplication.getAppTitle() != null && !spaceApplication.getAppTitle().isEmpty()) {
label = spaceApplication.getAppTitle();
}
if (isRoot) {
pageName = space.getUrl();
label = space.getDisplayName();
} else {
if (spaceApplication.getUri() != null && !spaceApplication.getUri().isEmpty()) {
pageName = spaceApplication.getUri();
}
}
UserPortal userPortal = SpaceUtils.getUserPortal();
userPortal.updateNode(parentNode, Scope.CHILDREN, null);
UserNode childNode = parentNode.addChild(pageName);
//when apply Navigation, can not need to setName for childNode. It's automatically.
//childNode.setName(pageName);
if (spaceApplication.getIcon() != null && !spaceApplication.getIcon().isEmpty()) {
childNode.setIcon(spaceApplication.getIcon());
}
childNode.setLabel(label);
childNode.setPageRef(page.getPageId());
try {
//only save with type is transient status
userPortal.saveNode(parentNode, null);
} catch (NavigationServiceException nex) {
LOG.info("Could not create page node from Application " + nex.toString());
return null;
} catch (Exception ex) {
LOG.info("Could not create page node from Application ", ex);
return null;
}
return childNode;
}
/**
* Retrieving the UserPortal
* @return
*/
private UserPortal getUserPortal() {
return Util.getUIPortalApplication().getUserPortalConfig().getUserPortal();
}
/**
* Gets an application by its id.
*
* @param space
* @param appId
* @return
* @throws SpaceException
*/
private Application getApplication(Space space, String appId) throws SpaceException {
Application app = getApplication(appCache, appId);
if (app == null) {
try {
if (appStoreCache == null) {
appStoreCache = SpaceUtils.getAppStore(space);
}
app = getApplication(appStoreCache, appId);
if (app == null) {
// retry
appStoreCache = SpaceUtils.getAppStore(space);
app = getApplication(appStoreCache, appId);
if (app == null) {
app = SpaceUtils.getAppFromPortalContainer(appId);
if (app == null) {
throw new Exception("app is null!");
}
}
}
appCache.add(app);
} catch (Exception e) {
throw new SpaceException(SpaceException.Code.UNABLE_TO_LIST_AVAILABLE_APPLICATIONS, e);
}
}
return app;
}
@SuppressWarnings("unchecked")
private void setPage(Space space,
Application app,
org.exoplatform.portal.config.model.Application<Gadget> gadgetApplication,
org.exoplatform.portal.config.model.Application<Portlet> portletApplication,
Page page) {
String visibility = space.getVisibility();
if (visibility.equals(Space.PUBLIC)) {
page.setAccessPermissions(new String[]{UserACL.EVERYONE});
} else {
page.setAccessPermissions(new String[]{"*:" + space.getGroupId()});
}
page.setEditPermission("manager:" + space.getGroupId());
ArrayList<ModelObject> pageChilds = page.getChildren();
Container menuContainer = SpaceUtils.findContainerById(pageChilds, SpaceUtils.MENU_CONTAINER);
org.exoplatform.portal.config.model.Application<Portlet> menuPortlet =
(org.exoplatform.portal.config.model.Application<Portlet>) menuContainer.getChildren()
.get(0);
ApplicationState<Portlet> state = menuPortlet.getState();
Portlet portletPreference = null;
try {
portletPreference = dataStorage.load(state, ApplicationType.PORTLET);
if (portletPreference == null) {
portletPreference = new PortletBuilder().add(SpaceUtils.SPACE_URL, space.getUrl()).build();
} else {
portletPreference.setValue(SpaceUtils.SPACE_URL, space.getUrl());
}
dataStorage.save(state, portletPreference);
} catch (Exception e) {
LOG.warn("Failed to setPage", e);
}
Container container = SpaceUtils.findContainerById(pageChilds, SpaceUtils.APPLICATION_CONTAINER);
ArrayList<ModelObject> children = container.getChildren();
if (app.getType() == ApplicationType.GADGET) {
children.add(gadgetApplication);
} else {
children.add(portletApplication);
}
container.setChildren(children);
pageChilds = setContainerById(pageChilds, container);
page.setChildren(pageChilds);
setPermissionForPage(page.getChildren(), "*:" + space.getGroupId());
}
/**
* Gets an application from appStore by appId.
*
* @param appStore
* @param appId
* @return app or null
*/
private Application getApplication(Map<ApplicationCategory, List<Application>> appStore,
String appId) {
Iterator<ApplicationCategory> categoryItr = appStore.keySet().iterator();
while (categoryItr.hasNext()) {
ApplicationCategory category = categoryItr.next();
List<Application> appList = category.getApplications();
Iterator<Application> appListItr = appList.iterator();
while (appListItr.hasNext()) {
Application app = appListItr.next();
if (app.getApplicationName().equals(appId)) {
return app;
}
}
}
return null;
}
/**
* Sets permission for page.
*
* @param children
* @param perm
* @return
*/
@SuppressWarnings("unchecked")
private void setPermissionForPage(ArrayList<ModelObject> children, String perm) {
for (ModelObject modelObject : children) {
if (modelObject instanceof org.exoplatform.portal.config.model.Application<?>) {
((org.exoplatform.portal.config.model.Application) modelObject).setAccessPermissions(new String[]{perm});
} else if (modelObject instanceof Container) {
((Container) modelObject).setAccessPermissions(new String[]{perm});
setPermissionForPage(((Container) modelObject).getChildren(), perm);
}
}
}
/**
* Sets container by Id
*
* @param childs
* @param container
* @return
*/
private ArrayList<ModelObject> setContainerById(ArrayList<ModelObject> childs, Container container) {
ArrayList<ModelObject> result = childs;
int index = result.indexOf(container);
//if container existing and child of the page
if (index != -1) {
result.set(index, container);
} else {
for (int i = 0; i < result.size(); i++) {
ModelObject obj = result.get(i);
if (org.exoplatform.portal.config.model.Application.class.isInstance(obj)) {
continue;
}
Container objContainer = (Container) obj;
ArrayList<ModelObject> tmp = setContainerById(objContainer.getChildren(), container);
objContainer.setChildren(tmp);
result.set(i, objContainer);
}
}
return result;
}
/**
* Creates portlet application from instanceId
*
* @param instanceId
* @return
*/
private org.exoplatform.portal.config.model.Application<Portlet> createPortletApplication(String instanceId,
Space space,
boolean isRoot) {
int i0 = instanceId.indexOf("#");
int i1 = instanceId.indexOf(":/", i0 + 1);
String ownerType = instanceId.substring(0, i0);
String ownerId = instanceId.substring(i0 + 1, i1);
String persistenceid = instanceId.substring(i1 + 2);
String[] persistenceChunks = Utils.split("/", persistenceid);
PortletBuilder pb = new PortletBuilder();
SpaceService spaceSrv = getSpaceService();
String[] appNames = spaceSrv.getPortletsPrefsRequired();
for (String appName : appNames) {
if (instanceId.contains(appName)) {
pb.add(SpaceUtils.SPACE_URL, space.getUrl());
break;
}
}
List<SpaceApplication> spaceApplicationList = spaceService.getSpaceApplicationConfigPlugin().getSpaceApplicationList();
SpaceApplication spaceApplication = null;
for (Iterator<SpaceApplication> iterator = spaceApplicationList.iterator(); iterator.hasNext() && spaceApplication == null;) {
SpaceApplication tmpSpaceApplication = iterator.next();
if (instanceId.contains(tmpSpaceApplication.getPortletName())) {
spaceApplication = tmpSpaceApplication;
}
}
if (spaceApplication != null && spaceApplication.getPreferences() != null) {
Set<Entry<String, String>> entrySet = spaceApplication.getPreferences().entrySet();
try {
for (Map.Entry<String, String> preference : entrySet) {
pb.add(preference.getKey(), getSubstituteValueFromPattern(space, spaceApplication, preference.getValue()));
}
} catch (Exception exception) {
LOG.warn(exception.getMessage(), exception);
}
}
TransientApplicationState<Portlet> portletState = new TransientApplicationState<Portlet>(persistenceChunks[0]
+ "/"
+ persistenceChunks[1],
pb.build(),
ownerType,
ownerId,
persistenceChunks[2]);
org.exoplatform.portal.config.model.Application<Portlet> portletApp =
org.exoplatform.portal.config.model.Application.createPortletApplication();
portletApp.setState(portletState);
return portletApp;
}
private String getSubstituteValueFromPattern(Space space, SpaceApplication spaceApplication, String pattern) {
if (!pattern.contains("{") || !pattern.contains("}")) {
return pattern;
}
if (pattern.contains(SPACE_URL_PREFERENCE)) {
pattern = pattern.replace(SPACE_URL_PREFERENCE, space.getUrl());
} else if (pattern.contains(GROUP_ID_PREFERENCE)) {
pattern = pattern.replace(GROUP_ID_PREFERENCE, space.getGroupId());
} else if (pattern.contains(MODIFIED_GROUP_ID_PREFERENCE)) {
String modifiedGroupId = space.getGroupId().replace("/", ".");
pattern = pattern.replace(MODIFIED_GROUP_ID_PREFERENCE, modifiedGroupId);
} else if (pattern.contains(PAGE_NAME_PREFERENCE)) {
pattern = pattern.replace(PAGE_NAME_PREFERENCE, spaceApplication.getAppTitle());
} else if (pattern.contains(PAGE_URL_PREFERENCE)) {
pattern = pattern.replace(PAGE_URL_PREFERENCE, spaceApplication.getUri());
}
return pattern;
}
/**
* Gets userPortalConfigService for the usage of creating new page from page template
*
* @return
*/
private UserPortalConfigService getUserPortalConfigService() {
return (UserPortalConfigService) container.getComponentInstanceOfType(UserPortalConfigService.class);
}
private SpaceService getSpaceService() {
ExoContainer container = ExoContainerContext.getCurrentContainer();
if (spaceService == null) {
spaceService = (SpaceService) container.getComponentInstance(SpaceService.class);
}
return spaceService;
}
}