/* * 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.taxonomy.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.jcr.AccessDeniedException; import javax.jcr.ItemExistsException; 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.UnsupportedRepositoryOperationException; import javax.jcr.Workspace; import javax.jcr.query.Query; import javax.jcr.query.QueryManager; import javax.jcr.query.QueryResult; import org.apache.commons.lang.StringUtils; import org.exoplatform.container.component.ComponentPlugin; import org.exoplatform.container.xml.InitParams; import org.exoplatform.container.xml.ObjectParameter; import org.exoplatform.container.xml.ValueParam; import org.exoplatform.services.cms.BasePath; import org.exoplatform.services.cms.impl.DMSConfiguration; import org.exoplatform.services.cms.impl.DMSRepositoryConfiguration; import org.exoplatform.services.cms.jcrext.activity.ActivityCommonService; import org.exoplatform.services.cms.link.LinkManager; import org.exoplatform.services.cms.taxonomy.TaxonomyService; import org.exoplatform.services.jcr.RepositoryService; import org.exoplatform.services.jcr.access.AccessControlEntry; import org.exoplatform.services.jcr.access.PermissionType; import org.exoplatform.services.jcr.config.RepositoryConfigurationException; 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.jcr.ext.hierarchy.NodeHierarchyCreator; 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; import org.picocontainer.Startable; /** * Created by The eXo Platform SARL Author : Ly Dinh Quang * quang.ly@exoplatform.com xxx5669@gmail.com Mar 31, 2009 */ public class TaxonomyServiceImpl implements TaxonomyService, Startable { private SessionProviderService providerService_; private NodeHierarchyCreator nodeHierarchyCreator_; private RepositoryService repositoryService_; private static final String TAXONOMY_LINK = "exo:taxonomyLink"; private static final String EXOSYMLINK_LINK = "exo:symlink"; private static final String EXO_WORKSPACE = "exo:workspace"; private static final String EXO_UUID = "exo:uuid"; private LinkManager linkManager_; private ListenerService listenerService; private ActivityCommonService activityService; private final String SQL_QUERY = "Select * from exo:taxonomyLink where jcr:path like '$0/%' " + "and exo:uuid = '$1' " + "and exo:workspace = '$2' " + "order by exo:dateCreated DESC"; private final String SQL_QUERY_EXACT_PATH = "Select * from exo:taxonomyLink where jcr:path like '$0/%' " + "and not jcr:path like '$0/%/%' " + "and exo:uuid = '$1' " + "and exo:workspace = '$2' " + "order by exo:dateCreated DESC"; List<TaxonomyPlugin> plugins_ = new ArrayList<TaxonomyPlugin>(); private DMSConfiguration dmsConfiguration_; private Map<String, String[]> taxonomyTreeDefaultUserPermissions_; private static final Log LOG = ExoLogger.getLogger(TaxonomyServiceImpl.class.getName()); private static String categoryNameLength_ = null; /** * Constructor method * @param providerService create session * @param nodeHierarchyCreator get path by alias name * @param repoService manage repository * @param linkManager create and reach link * @param dmsConfiguration get dms-system workspace * @throws Exception */ public TaxonomyServiceImpl(InitParams initParams, SessionProviderService providerService, NodeHierarchyCreator nodeHierarchyCreator, RepositoryService repoService, LinkManager linkManager, DMSConfiguration dmsConfiguration) throws Exception { providerService_ = providerService; nodeHierarchyCreator_ = nodeHierarchyCreator; repositoryService_ = repoService; linkManager_ = linkManager; dmsConfiguration_ = dmsConfiguration; ValueParam valueParam = initParams.getValueParam("categoryNameLength"); if(valueParam!=null) categoryNameLength_ = valueParam.getValue(); else categoryNameLength_ = "150"; ObjectParameter objectParam = initParams.getObjectParam("defaultPermission.configuration"); if (objectParam != null) taxonomyTreeDefaultUserPermissions_ = getPermissions(((TaxonomyTreeDefaultUserPermission)objectParam.getObject()).getPermissions()); activityService = WCMCoreUtils.getService(ActivityCommonService.class); } public String getCategoryNameLength() { return categoryNameLength_; } public void init() throws Exception { for (TaxonomyPlugin plugin : plugins_) { plugin.init(); } } /** * {@inheritDoc} */ public void addTaxonomyPlugin(ComponentPlugin plugin) { if (plugin instanceof TaxonomyPlugin) { plugins_.add((TaxonomyPlugin) plugin); } } public List<Node> getAllTaxonomyTrees() throws RepositoryException { return getAllTaxonomyTrees(false); } /** * {@inheritDoc} */ public List<Node> getAllTaxonomyTrees(boolean system) throws RepositoryException { List<Node> listNode = new ArrayList<Node>(); try { Node taxonomyDef = getRootTaxonomyDef(); NodeIterator nodeIter = taxonomyDef.getNodes(); while (nodeIter.hasNext()) { Node node = (Node) nodeIter.next(); if (node.isNodeType(EXOSYMLINK_LINK)) { try { Node target = linkManager_.getTarget(node, system); if (target != null) listNode.add(target); } catch (ItemNotFoundException ex) { continue; } catch (AccessDeniedException adex) { continue; } } } } catch (RepositoryConfigurationException e) { throw new RepositoryException(e); } return listNode; } /** * {@inheritDoc} */ public Node getTaxonomyTree(String taxonomyName) throws RepositoryException { return getTaxonomyTree(taxonomyName, false); } /** * {@inheritDoc} */ public Node getTaxonomyTree(String taxonomyName, boolean system) throws RepositoryException { try { Node taxonomyDef = getRootTaxonomyDef(); try { Node taxonomyTree = taxonomyDef.getNode(taxonomyName); if (taxonomyTree.isNodeType(EXOSYMLINK_LINK)) return linkManager_.getTarget(taxonomyTree, system); }catch (PathNotFoundException pne) { throw new RepositoryException(pne); } } catch (RepositoryConfigurationException e1) { throw new RepositoryException(e1); } catch (PathNotFoundException e2) { throw new RepositoryException(e2); } return null; } /** * {@inheritDoc} */ public boolean hasTaxonomyTree(String taxonomyName) throws RepositoryException { SessionProvider systemProvider = SessionProvider.createSystemProvider(); try { Node taxonomyTree = getRootTaxonomyDef(systemProvider).getNode(taxonomyName); return taxonomyTree.isNodeType(EXOSYMLINK_LINK); } catch (RepositoryConfigurationException e1) { throw new RepositoryException(e1); } catch (PathNotFoundException e2) { //ignore this exception } finally { systemProvider.close(); } return false; } /** * {@inheritDoc} */ public void addTaxonomyTree(Node taxonomyTree) throws RepositoryException, TaxonomyAlreadyExistsException { if (hasTaxonomyTree(taxonomyTree.getName())) { throw new TaxonomyAlreadyExistsException(); } SessionProvider systemProvider = SessionProvider.createSystemProvider(); try { Node taxonomyDef = getRootTaxonomyDef(systemProvider); linkManager_.createLink(taxonomyDef, EXOSYMLINK_LINK, taxonomyTree, taxonomyTree.getName()); } catch (RepositoryConfigurationException e) { throw new RepositoryException(e); } finally { systemProvider.close(); } } /** * {@inheritDoc} */ public void updateTaxonomyTree(String taxonomyName, Node taxonomyTree) throws RepositoryException { try { if (hasTaxonomyTree(taxonomyName)) { Node taxonomyTreeLink = getRootTaxonomyDef().getNode(taxonomyName); linkManager_.updateLink(taxonomyTreeLink, taxonomyTree); } } catch (RepositoryConfigurationException e) { throw new RepositoryException(e); } } /** * {@inheritDoc} */ public void removeTaxonomyTree(String taxonomyName) throws RepositoryException { Session session = null; try { if (hasTaxonomyTree(taxonomyName)) { Node targetNode = getTaxonomyTree(taxonomyName, true); session = targetNode.getSession(); targetNode.remove(); session.save(); Node taxonomyDef = getRootTaxonomyDef(); if (taxonomyDef.hasNode(taxonomyName)) { Node taxonomyTree = taxonomyDef.getNode(taxonomyName); taxonomyTree.remove(); taxonomyDef.getSession().save(); } } } catch (RepositoryConfigurationException e) { throw new RepositoryException(e); } } /** * {@inheritDoc} */ public void addTaxonomyNode(String workspace, String parentPath, String taxoNodeName, String creatorUser) throws RepositoryException, TaxonomyNodeAlreadyExistsException { Session systemSession = null; try { ManageableRepository manaRepo = repositoryService_.getCurrentRepository(); systemSession = getSession(manaRepo, workspace, true); Node parentNode = (Node) systemSession.getItem(parentPath); if (parentNode.hasNode(taxoNodeName)) throw new TaxonomyNodeAlreadyExistsException(); ExtendedNode node = (ExtendedNode) parentNode.addNode(taxoNodeName, "exo:taxonomy"); if (node.canAddMixin("exo:privilegeable")) { if(node.hasProperty("exo:owner")) { String owner = node.getProperty("exo:owner").getString(); node.addMixin("exo:privilegeable"); node.setPermission(owner, PermissionType.ALL); if (creatorUser != null) node.setPermission(creatorUser, PermissionType.ALL); for(Map.Entry<String, String[]> entry : taxonomyTreeDefaultUserPermissions_.entrySet()) { node.setPermission(entry.getKey(), entry.getValue()); } } if (!node.isNodeType("exo:privilegeable")) node.addMixin("exo:privilegeable"); String systemUser = IdentityConstants.SYSTEM; if (!containsUser(node.getACL().getPermissionEntries(), systemUser)) node.setPermission(systemUser, PermissionType.ALL); } systemSession.save(); } catch (PathNotFoundException e2) { throw new RepositoryException(e2); } } private boolean containsUser(List<AccessControlEntry> entries, String userName) { if (userName == null) return false; for (AccessControlEntry entry : entries) if (userName.equals(entry.getIdentity())) return true; return false; } /** * {@inheritDoc} */ public void removeTaxonomyNode(String workspace, String absPath) throws RepositoryException { Session systemSession = null; try { ManageableRepository manaRepo = repositoryService_.getCurrentRepository(); systemSession = getSession(manaRepo, workspace, true); Node taxonomyNode = (Node) systemSession.getItem(absPath); taxonomyNode.remove(); systemSession.save(); } catch (PathNotFoundException e2) { throw new RepositoryException(e2); } } /** * {@inheritDoc} */ public List<Node> getCategories(Node node, String taxonomyName) throws RepositoryException { return getCategories(node, taxonomyName, false); } /** * {@inheritDoc} */ public List<Node> getCategories(Node node, String taxonomyName, boolean system) throws RepositoryException { List<Node> listCate = new ArrayList<Node>(); Session session = null; try { if (node.isNodeType("mix:referenceable")) { Node rootNodeTaxonomy = getTaxonomyTree(taxonomyName, system); if (rootNodeTaxonomy != null) { String sql = null; sql = StringUtils.replace(SQL_QUERY, "$0", rootNodeTaxonomy.getPath()); sql = StringUtils.replace(sql, "$1", node.getUUID()); sql = StringUtils.replace(sql, "$2", node.getSession().getWorkspace().getName()); session = repositoryService_.getCurrentRepository().login(rootNodeTaxonomy.getSession().getWorkspace().getName()); QueryManager queryManager = session.getWorkspace().getQueryManager(); Query query = queryManager.createQuery(sql, Query.SQL); QueryResult result = query.execute(); NodeIterator iterate = result.getNodes(); Set<String> addedNode = new HashSet<String>(); while (iterate.hasNext()) { Node parentCate = iterate.nextNode().getParent(); // We need filtering duplicated result to fix the problem of ECMS-3282. if (!addedNode.contains(parentCate.getSession().getWorkspace().getName() + ":/" + parentCate.getPath())) { listCate.add(parentCate); addedNode.add(parentCate.getSession().getWorkspace().getName() + ":/" + parentCate.getPath()); } } } } } catch (Exception e) { throw new RepositoryException(e); } finally { if(session != null) session.logout(); } return listCate; } /** * {@inheritDoc} */ public List<Node> getAllCategories(Node node) throws RepositoryException { return getAllCategories(node, false); } /** * {@inheritDoc} */ public List<Node> getAllCategories(Node node, boolean system) throws RepositoryException { List<Node> listCategories = new ArrayList<Node>(); List<Node> allTrees = getAllTaxonomyTrees(system); for (Node tree : allTrees) { List<Node> categories = getCategories(node, tree.getName(), system); for (Node category : categories) listCategories.add(category); } return listCategories; } /** * {@inheritDoc} */ public void addCategory(Node node, String taxonomyName, String categoryPath) throws RepositoryException { addCategories(node, taxonomyName, new String[] { categoryPath }); } /** * {@inheritDoc} */ public void addCategory(Node node, String taxonomyName, String categoryPath, boolean system) throws RepositoryException { addCategories(node, taxonomyName, new String[] { categoryPath }, system); } /** * {@inheritDoc} */ public void addCategories(Node node, String taxonomyName, String[] categoryPaths) throws RepositoryException { addCategories(node, taxonomyName, categoryPaths, false); } /** * {@inheritDoc} */ public void addCategories(Node node, String taxonomyName, String[] categoryPaths, boolean system) throws RepositoryException { if (listenerService ==null) { listenerService = WCMCoreUtils.getService(ListenerService.class); } String category = ""; try { Node rootNodeTaxonomy = getTaxonomyTree(taxonomyName, system); for (String categoryPath : categoryPaths) { //get category path if (rootNodeTaxonomy.getPath().equals("/")) { category = categoryPath; } else if (categoryPath.length() != 0) { if (!categoryPath.startsWith("/")) category = rootNodeTaxonomy.getPath() + "/" + categoryPath; else category = rootNodeTaxonomy.getPath() + categoryPath; } else { category = rootNodeTaxonomy.getPath(); } //get category node Node categoryNode; if (categoryPath.startsWith(rootNodeTaxonomy.getPath())) { categoryNode = (Node) rootNodeTaxonomy.getSession().getItem(categoryPath); } else if (categoryPath.equals("")) { categoryNode = rootNodeTaxonomy; } else { categoryNode = (Node) rootNodeTaxonomy.getSession().getItem(category); } String categoryName = categoryNode.getName(); if (categoryNode.hasProperty("exo:title")) { categoryName = categoryNode.getProperty("exo:title").getString(); } //add mix referenceable for node if (node.canAddMixin("mix:referenceable")) { node.addMixin("mix:referenceable"); node.getSession().save(); } //generate unique linkName String nodeUUID = node.getUUID(); String nodeWS = node.getSession().getWorkspace().getName(); String linkName = node.getName(); int index = 1; while (categoryNode.hasNode(linkName)) { Node taxonomyNode = categoryNode.getNode(linkName); if (nodeUUID.equals(taxonomyNode.getProperty(EXO_UUID).getString()) && nodeWS.equals(taxonomyNode.getProperty(EXO_WORKSPACE).getString())) { throw new ItemExistsException(); } linkName = node.getName() + index++; } //create link linkManager_.createLink(categoryNode, TAXONOMY_LINK, node, linkName); if (listenerService!=null) { try { if (activityService.isAcceptedNode(node) || (node.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE) && activityService.isBroadcastNTFileEvents(node))) { listenerService.broadcast(ActivityCommonService.CATEGORY_ADDED_ACTIVITY, node, categoryName); } } catch (Exception e) { if (LOG.isErrorEnabled()) { LOG.error("Can not notify CategoryAddedActivity because of: " + e.getMessage()); } } } } } catch (PathNotFoundException e) { throw new RepositoryException(e); } } /** * {@inheritDoc} */ public boolean hasCategories(Node node, String taxonomyName) throws RepositoryException { return hasCategories(node, taxonomyName, false); } /** * {@inheritDoc} */ public boolean hasCategories(Node node, String taxonomyName, boolean system) throws RepositoryException { List<Node> listCate = getCategories(node, taxonomyName, system); if (listCate != null && listCate.size() > 0) return true; return false; } public void moveTaxonomyNode(String workspace, String srcPath, String destPath, String type) throws RepositoryException { Session systemSession = null; try { ManageableRepository manaRepo = repositoryService_.getCurrentRepository(); systemSession = getSession(manaRepo, workspace, true); if ("cut".equals(type)) { systemSession.move(srcPath, destPath); systemSession.save(); } else if ("copy".equals(type)) { Workspace wspace = systemSession.getWorkspace(); wspace.copy(srcPath, destPath); systemSession.save(); } else throw new UnsupportedRepositoryOperationException(); } finally { } } /** * {@inheritDoc} */ public void removeCategory(Node node, String taxonomyName, String categoryPath) throws RepositoryException { removeCategory(node, taxonomyName, categoryPath, false); } /** * {@inheritDoc} */ public void removeCategory(Node node, String taxonomyName, String categoryPath, boolean system) throws RepositoryException { try { //get category node String category = ""; Node rootNodeTaxonomy = getTaxonomyTree(taxonomyName, system); if (rootNodeTaxonomy.getPath().equals("/")) { category = categoryPath; } else if (!categoryPath.startsWith("/")) { category = rootNodeTaxonomy.getPath() + "/" + categoryPath; } else { category = rootNodeTaxonomy.getPath() + categoryPath; } Node categoryNode = ((Node) rootNodeTaxonomy.getSession().getItem(category)); String categoryName = categoryNode.getName(); //get taxonomyLink node String sql = StringUtils.replace(SQL_QUERY_EXACT_PATH, "$0", categoryNode.getPath()); sql = StringUtils.replace(sql, "$1", node.getUUID()); sql = StringUtils.replace(sql, "$2", node.getSession().getWorkspace().getName()); QueryManager queryManager = categoryNode.getSession().getWorkspace().getQueryManager(); Query query = queryManager.createQuery(sql, Query.SQL); QueryResult result = query.execute(); NodeIterator iterate = result.getNodes(); Node nodeTaxonomyLink = null; if (iterate != null && iterate.hasNext()) { nodeTaxonomyLink = iterate.nextNode(); } //remove taxonomyLink node if (nodeTaxonomyLink == null) { throw new RepositoryException("canot found taxonomy link node"); } nodeTaxonomyLink.remove(); categoryNode.save(); node.getSession().save(); if (listenerService!=null) { try { if (activityService.isAcceptedNode(node) || (node.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE) && activityService.isBroadcastNTFileEvents(node))) { listenerService.broadcast(ActivityCommonService.CATEGORY_REMOVED_ACTIVITY, node, categoryName); } } catch (Exception e) { if (LOG.isErrorEnabled()) { LOG.error("Can not notify Activity because of: " + e.getMessage()); } } } } catch (PathNotFoundException e) { throw new RepositoryException(e); } } /** * {@inheritDoc} */ public Map<String, String[]> getTaxonomyTreeDefaultUserPermission() { return taxonomyTreeDefaultUserPermissions_; } public Map<String, String[]> getPermissions(List<TaxonomyTreeDefaultUserPermission.Permission> permissions) { Map<String, String[]> permissionsMap = new HashMap<String, String[]>(); for (TaxonomyTreeDefaultUserPermission.Permission permission : permissions) { StringBuilder strPer = new StringBuilder(); if ("true".equals(permission.getRead())) strPer.append(PermissionType.READ); if ("true".equals(permission.getAddNode())) strPer.append(",").append(PermissionType.ADD_NODE); if ("true".equals(permission.getSetProperty())) strPer.append(",").append(PermissionType.SET_PROPERTY); if ("true".equals(permission.getRemove())) strPer.append(",").append(PermissionType.REMOVE); permissionsMap.put(permission.getIdentity(), strPer.toString().split(",")); } return permissionsMap; } /** * Get node as root of all taxonomy in the repository that is in TAXONOMIES_TREE_DEFINITION_PATH * @param systemProvider System Provider * @return * @throws RepositoryException * @throws RepositoryConfigurationException */ private Node getRootTaxonomyDef(SessionProvider systemProvider) throws RepositoryException, RepositoryConfigurationException { ManageableRepository manaRepository = repositoryService_.getCurrentRepository(); DMSRepositoryConfiguration dmsRepoConfig = dmsConfiguration_.getConfig(); Session systemSession = systemProvider.getSession(dmsRepoConfig.getSystemWorkspace(), manaRepository); String taxonomiesTreeDef = nodeHierarchyCreator_ .getJcrPath(BasePath.TAXONOMIES_TREE_DEFINITION_PATH); Node taxonomyRootDef = (Node) systemSession.getItem(taxonomiesTreeDef); return taxonomyRootDef; } /** * Get node as root of all taxonomy in the repository that is in TAXONOMIES_TREE_DEFINITION_PATH * @return * @throws RepositoryException * @throws RepositoryConfigurationException */ private Node getRootTaxonomyDef() throws RepositoryException, RepositoryConfigurationException { ManageableRepository manaRepository = repositoryService_.getCurrentRepository(); DMSRepositoryConfiguration dmsRepoConfig = dmsConfiguration_.getConfig(); Session systemSession = getSession(manaRepository, dmsRepoConfig.getSystemWorkspace(), true); String taxonomiesTreeDef = nodeHierarchyCreator_ .getJcrPath(BasePath.TAXONOMIES_TREE_DEFINITION_PATH); Node taxonomyRootDef = (Node) systemSession.getItem(taxonomiesTreeDef); return taxonomyRootDef; } /** * Get session by workspace and ManageableRepository * @param manageRepository * @param workspaceName * @param system * @return System session if system = true, else return session of current user * @throws RepositoryException */ 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 void start() { try { for (TaxonomyPlugin plugin : plugins_) { plugin.init() ; } } catch (Exception e) { if (LOG.isErrorEnabled()) { LOG.error("Unexpected error", e); } } } /** * {@inheritDoc} */ public void stop() { } }