LinkManagerImpl.java
/*
* 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.services.cms.link.impl;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.jcr.Item;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.ValueFormatException;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
import org.exoplatform.services.cms.CmsService;
import org.exoplatform.services.cms.impl.Utils;
import org.exoplatform.services.cms.link.LinkManager;
import org.exoplatform.services.cms.link.NodeLinkAware;
import org.exoplatform.services.jcr.access.AccessControlEntry;
import org.exoplatform.services.jcr.access.PermissionType;
import org.exoplatform.services.jcr.core.ExtendedNode;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.jcr.ext.app.SessionProviderService;
import org.exoplatform.services.jcr.ext.common.SessionProvider;
import org.exoplatform.services.listener.ListenerService;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.security.IdentityConstants;
import org.exoplatform.services.wcm.core.NodetypeConstant;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;
/**
* Created by The eXo Platform SARL
* Author : Ly Dinh Quang
* quang.ly@exoplatform.com
* Mar 13, 2009
*/
public class LinkManagerImpl implements LinkManager {
private final static String SYMLINK = "exo:symlink";
private final static String WORKSPACE = "exo:workspace";
private final static String UUID = "exo:uuid";
private final static String PRIMARY_TYPE = "exo:primaryType";
private final static String SYMLINK_NAME = "exo:name";
private final static String SYMLINK_TITLE = "exo:title";
private final static Log LOG = ExoLogger.getLogger(LinkManagerImpl.class.getName());
private final SessionProviderService providerService_;
public LinkManagerImpl(SessionProviderService providerService) throws Exception {
providerService_ = providerService;
}
/**
* {@inheritDoc}
*/
public Node createLink(Node parent, String linkType, Node target) throws RepositoryException {
return createLink(parent, linkType, target, null);
}
/**
* {@inheritDoc}
*/
public Node createLink(Node parent, Node target) throws RepositoryException {
return createLink(parent, null, target, null);
}
/**
* {@inheritDoc}
*/
public Node createLink(Node parent, String linkType, Node target, String linkName) throws RepositoryException {
return createLink(parent, linkType, target, linkName, linkName);
}
/**
* {@inheritDoc}
*/
public Node createLink(Node parent, String linkType, Node target, String linkName, String linkTitle)
throws RepositoryException {
if (!target.isNodeType(SYMLINK)) {
boolean targetEdited = false;
if (target.canAddMixin("mix:referenceable")) {
target.addMixin("mix:referenceable");
target.getSession().save();
targetEdited = true;
}
if (linkType == null || linkType.trim().length() == 0)
linkType = SYMLINK;
if (linkName == null || linkName.trim().length() == 0)
linkName = target.getName();
Node linkNode = parent.addNode(linkName, linkType);
try {
updateAccessPermissionToLink(linkNode, target);
} catch(Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error("CAN NOT UPDATE ACCESS PERMISSIONS FROM TARGET NODE TO LINK NODE", e);
}
}
linkNode.setProperty(WORKSPACE, target.getSession().getWorkspace().getName());
linkNode.setProperty(PRIMARY_TYPE, target.getPrimaryNodeType().getName());
linkNode.setProperty(UUID, target.getUUID());
if(linkNode.canAddMixin("exo:sortable")) {
linkNode.addMixin("exo:sortable");
}
linkNode.setProperty(SYMLINK_TITLE, linkTitle);
linkNode.setProperty(SYMLINK_NAME, linkName);
linkNode.getSession().save();
ListenerService listenerService = WCMCoreUtils.getService(ListenerService.class);
try {
String remoteUser = WCMCoreUtils.getRemoteUser();
if (remoteUser != null) {
if (Utils.isDocument(target) && targetEdited) {
listenerService.broadcast(CmsService.POST_EDIT_CONTENT_EVENT, null, target);
}
}
} catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error("Error while broadcasting event: " + e.getMessage());
}
}
return linkNode;
}
return null;
}
/**
* {@inheritDoc}
*/
public Node getTarget(Node link, boolean system) throws ItemNotFoundException,
RepositoryException {
String uuid = link.getProperty(UUID).getString();
Node targetNode = getSession(link, system).getNodeByUUID(uuid);
return targetNode;
}
private Session getSession(Node link, boolean system) throws RepositoryException {
String workspaceTarget = link.getProperty(WORKSPACE).getString();
return getSession((ManageableRepository) link.getSession().getRepository(), workspaceTarget,
system);
}
private Session getSession(ManageableRepository manageRepository, String workspaceName,
boolean system) throws RepositoryException {
if (system)
return providerService_.getSystemSessionProvider(null).getSession(workspaceName, manageRepository);
return providerService_.getSessionProvider(null).getSession(workspaceName, manageRepository);
}
/**
* {@inheritDoc}
*/
public Node getTarget(Node link) throws ItemNotFoundException, RepositoryException {
return getTarget(link, false);
}
/**
* {@inheritDoc}
*/
public boolean isTargetReachable(Node link) throws RepositoryException {
return isTargetReachable(link, false);
}
/**
* {@inheritDoc}
*/
public boolean isTargetReachable(Node link, boolean system) throws RepositoryException {
Session session = null;
try {
session = getSession(link, system);
session.getNodeByUUID(link.getProperty(UUID).getString());
} catch (ItemNotFoundException e) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
public Node updateLink(Node linkNode, Node targetNode) throws RepositoryException {
if (targetNode.canAddMixin("mix:referenceable")) {
targetNode.addMixin("mix:referenceable");
targetNode.getSession().save();
}
try {
updateAccessPermissionToLink(linkNode, targetNode);
} catch(Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error("CAN NOT UPDATE ACCESS PERMISSIONS FROM TARGET NODE TO LINK NODE", e);
}
}
linkNode.setProperty(UUID, targetNode.getUUID());
linkNode.setProperty(PRIMARY_TYPE, targetNode.getPrimaryNodeType().getName());
linkNode.setProperty(WORKSPACE, targetNode.getSession().getWorkspace().getName());
linkNode.getSession().save();
return linkNode;
}
/**
* {@inheritDoc}
*/
public boolean isLink(Item item) throws RepositoryException {
if (item instanceof Node) {
Node node = (Node) item;
if (node instanceof NodeLinkAware) {
node = ((NodeLinkAware) node).getRealNode();
}
return node.isNodeType(SYMLINK);
}
return false;
}
/**
* {@inheritDoc}
*/
public String getTargetPrimaryNodeType(Node link) throws RepositoryException {
return link.getProperty(PRIMARY_TYPE).getString();
}
/**
* Update the permission between two given node
* @param linkNode The node to update permission
* @param targetNode The target node to get permission
* @throws Exception
*/
private void updateAccessPermissionToLink(Node linkNode, Node targetNode) throws Exception {
if(canChangePermission(linkNode)) {
if(linkNode.canAddMixin("exo:privilegeable")) {
linkNode.addMixin("exo:privilegeable");
((ExtendedNode)linkNode).setPermission(getNodeOwner(linkNode),PermissionType.ALL);
}
removeCurrentIdentites(linkNode);
Map<String, String[]> perMap = new HashMap<String, String[]>();
List<String> permsList = new ArrayList<String>();
List<String> idList = new ArrayList<String>();
for(AccessControlEntry accessEntry : ((ExtendedNode)targetNode).getACL().getPermissionEntries()) {
if(!idList.contains(accessEntry.getIdentity())) {
idList.add(accessEntry.getIdentity());
permsList = ((ExtendedNode)targetNode).getACL().getPermissions(accessEntry.getIdentity());
perMap.put(accessEntry.getIdentity(), permsList.toArray(new String[permsList.size()]));
}
}
((ExtendedNode)linkNode).setPermissions(perMap);
}
}
/**
* Remove all identity of the given node
* @param linkNode The node to remove all identity
* @throws AccessControlException
* @throws RepositoryException
*/
private void removeCurrentIdentites(Node linkNode) throws AccessControlException, RepositoryException {
String currentUser = linkNode.getSession().getUserID();
if (currentUser != null)
((ExtendedNode)linkNode).setPermission(currentUser, PermissionType.ALL);
for(AccessControlEntry accessEntry : ((ExtendedNode)linkNode).getACL().getPermissionEntries()) {
if(canRemovePermission(linkNode, accessEntry.getIdentity())
&& ((ExtendedNode)linkNode).getACL().getPermissions(accessEntry.getIdentity()).size() > 0
&& !accessEntry.getIdentity().equals(currentUser)) {
((ExtendedNode) linkNode).removePermission(accessEntry.getIdentity());
}
}
}
/**
* Remove the permission from the given node
* @param node The node to remove permission
* @param identity The identity of the permission
* @return
* @throws ValueFormatException
* @throws PathNotFoundException
* @throws RepositoryException
*/
private boolean canRemovePermission(Node node, String identity) throws ValueFormatException,
PathNotFoundException, RepositoryException {
String owner = getNodeOwner(node);
if(identity.equals(IdentityConstants.SYSTEM)) return false;
if(owner != null && owner.equals(identity)) return false;
return true;
}
/**
* Get the owner of the given node
* @param node The node to get owner
* @return
* @throws ValueFormatException
* @throws PathNotFoundException
* @throws RepositoryException
*/
private String getNodeOwner(Node node) throws ValueFormatException, PathNotFoundException, RepositoryException {
if(node.hasProperty("exo:owner")) {
return node.getProperty("exo:owner").getString();
}
return IdentityConstants.SYSTEM;
}
/**
* Check permission of the given node
* @param node The Node to check permission
* @return
* @throws RepositoryException
*/
private boolean canChangePermission(Node node) throws RepositoryException {
try {
((ExtendedNode)node).checkPermission(PermissionType.CHANGE_PERMISSION);
return true;
} catch(AccessControlException e) {
return false;
}
}
/**
* {@inheritDoc}
*/
public List<Node> getAllLinks(Node targetNode, String linkType, SessionProvider sessionProvider) {
try {
List<Node> result = new ArrayList<Node>();
ManageableRepository repository = WCMCoreUtils.getRepository();
String[] workspaces = repository.getWorkspaceNames();
String queryString = new StringBuilder().append("SELECT * FROM ").
append(linkType).
append(" WHERE exo:uuid='").
append(targetNode.getUUID()).append("'").
append(" AND exo:workspace='").
append(targetNode.getSession().getWorkspace().getName()).
append("'").toString();
for (String workspace : workspaces) {
Session session = sessionProvider.getSession(workspace, repository);
//Continue In the case cannot access to a workspace
if(session == null) continue;
QueryManager queryManager = session.getWorkspace().getQueryManager();
Query query = queryManager.createQuery(queryString, Query.SQL);
QueryResult queryResult = query.execute();
NodeIterator iter = queryResult.getNodes();
while (iter.hasNext()) {
result.add(iter.nextNode());
}
}
return result;
} catch (RepositoryException e) {
// return empty node list if there are errors in execution or user has no right to access nodes
return new ArrayList<Node>();
}
}
/**
* {@inheritDoc}
*/
public List<Node> getAllLinks(Node targetNode, String linkType) {
return getAllLinks(targetNode, linkType, WCMCoreUtils.getUserSessionProvider());
}
/**
* {@inheritDoc}
* @throws RepositoryException
*/
public void updateSymlink(Node node) throws RepositoryException {
if (node.isNodeType(NodetypeConstant.EXO_SYMLINK)) {
try {
((ExtendedNode)node).checkPermission(PermissionType.SET_PROPERTY);
} catch(AccessControlException e) {
SessionProvider provider = WCMCoreUtils.getSystemSessionProvider();
node = (Node)provider.getSession(node.getSession().getWorkspace().getName(),
WCMCoreUtils.getRepository()).getItem(node.getPath());
}
if (node.canAddMixin(NodetypeConstant.EXO_TARGET_DATA)) {
node.addMixin(NodetypeConstant.EXO_TARGET_DATA);
}
Node target = this.getTarget(node, true);
if (!node.hasProperty(NodetypeConstant.EXO_LAST_MODIFIED_DATE) ||
node.getProperty(NodetypeConstant.EXO_LAST_MODIFIED_DATE).getDate().compareTo(
target.getProperty(NodetypeConstant.EXO_LAST_MODIFIED_DATE).getDate()) < 0) {
String[] propList = {NodetypeConstant.EXO_DATE_CREATED,
NodetypeConstant.EXO_LAST_MODIFIED_DATE, NodetypeConstant.PUBLICATION_LIVE_DATE,
NodetypeConstant.EXO_START_EVENT, NodetypeConstant.EXO_INDEX};
for (String p : propList) {
try {
if (target.hasProperty(p)) {
node.setProperty(p, target.getProperty(p).getValue());
node.save();
}
} catch (RepositoryException e) {
if (LOG.isErrorEnabled()) {
LOG.error("Can not update property: " + p + " for node: " + node.getPath(), e);
}
}
}
}
}
}
}