Java tutorial
/** * ========================================================================================== * = JAHIA'S DUAL LICENSING - IMPORTANT INFORMATION = * ========================================================================================== * * http://www.jahia.com * * Copyright (C) 2002-2017 Jahia Solutions Group SA. All rights reserved. * * THIS FILE IS AVAILABLE UNDER TWO DIFFERENT LICENSES: * 1/GPL OR 2/JSEL * * 1/ GPL * ================================================================================== * * IF YOU DECIDE TO CHOOSE THE GPL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS: * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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/>. * * * 2/ JSEL - Commercial and Supported Versions of the program * =================================================================================== * * IF YOU DECIDE TO CHOOSE THE JSEL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS: * * Alternatively, commercial and supported versions of the program - also known as * Enterprise Distributions - must be used in accordance with the terms and conditions * contained in a separate written agreement between you and Jahia Solutions Group SA. * * If you are unsure which license is appropriate for your use, * please contact the sales department at sales@jahia.com. */ package org.jahia.services.content; import net.htmlparser.jericho.Source; import net.htmlparser.jericho.TextExtractor; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.mutable.MutableInt; import org.apache.jackrabbit.commons.iterator.PropertyIteratorAdapter; import org.apache.jackrabbit.core.JahiaSessionImpl; import org.apache.jackrabbit.core.NodeImpl; import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl; import org.apache.jackrabbit.util.ChildrenCollectorFilter; import org.apache.jackrabbit.util.Text; import org.jahia.api.Constants; import org.jahia.bin.Jahia; import org.jahia.registries.ServicesRegistry; import org.jahia.services.content.decorator.*; import org.jahia.services.content.nodetypes.ExtendedNodeDefinition; import org.jahia.services.content.nodetypes.ExtendedNodeType; import org.jahia.services.content.nodetypes.ExtendedPropertyDefinition; import org.jahia.services.content.nodetypes.NodeTypeRegistry; import org.jahia.services.importexport.ReferencesHelper; import org.jahia.services.visibility.VisibilityService; import org.jahia.settings.SettingsBean; import org.jahia.utils.LanguageCodeConverters; import org.jahia.utils.i18n.Messages; import org.slf4j.Logger; import javax.jcr.*; import javax.jcr.lock.Lock; import javax.jcr.lock.LockException; import javax.jcr.nodetype.ConstraintViolationException; import javax.jcr.nodetype.NoSuchNodeTypeException; import javax.jcr.nodetype.NodeDefinition; import javax.jcr.nodetype.NodeType; import javax.jcr.query.Query; import javax.jcr.security.AccessControlManager; import javax.jcr.security.Privilege; import javax.jcr.version.*; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import java.io.InputStream; import java.math.BigDecimal; import java.util.*; import java.util.regex.Pattern; import static org.jahia.api.Constants.*; /** * Wrappers around <code>javax.jcr.Node</code> to be able to inject * Jahia specific actions. * * @author toto */ public class JCRNodeWrapperImpl extends JCRItemWrapperImpl implements JCRNodeWrapper { protected static final Logger logger = org.slf4j.LoggerFactory.getLogger(JCRNodeWrapperImpl.class); private static final String[] TRANSLATION_NODES_PATTERN = new String[] { "j:translation_*" }; private static final String TRANSLATION_PREFIX = "j:translation_"; protected Node objectNode = null; protected JCRFileContent fileContent = null; protected JCRSiteNode site = null; protected boolean parentAlreadyResolved = false; protected JCRNodeWrapper resolvedParentNode = null; protected Map<Locale, Node> i18NobjectNodes = null; public static final String EXTERNAL_IDENTIFIER_PROP_NAME_SEPARATOR = "___"; public static final Pattern EXTERNAL_IDENTIFIER_PROP_NAME_SEPARATOR_PATTERN = Pattern .compile(EXTERNAL_IDENTIFIER_PROP_NAME_SEPARATOR); private static final ExtendedNodeType[] EMPTY_EXTENDED_NODE_TYPE_ARRAY = new ExtendedNodeType[0]; private Map<String, ExtendedPropertyDefinition> applicablePropertyDefinition = new HashMap<String, ExtendedPropertyDefinition>(); private Map<String, Boolean> hasPropertyCache = new HashMap<String, Boolean>(); private static boolean doCopy(JCRNodeWrapper source, JCRNodeWrapper dest, String name, boolean allowsExternalSharedNodes, Map<String, List<String>> references, List<String> ignoreNodeTypes, int maxBatch, MutableInt batchCount, boolean isTopObject) throws RepositoryException { if (source instanceof JCRNodeWrapperImpl) { return ((JCRNodeWrapperImpl) source).internalCopy(dest, name, allowsExternalSharedNodes, references, ignoreNodeTypes, maxBatch, batchCount, isTopObject); } else if (source instanceof JCRNodeDecorator) { return ((JCRNodeDecorator) source).internalCopy(dest, name, allowsExternalSharedNodes, references, ignoreNodeTypes, maxBatch, batchCount, isTopObject); } else { return source.copy(dest, name, allowsExternalSharedNodes, references, ignoreNodeTypes, maxBatch, batchCount); } } protected JCRNodeWrapperImpl(Node objectNode, String path, JCRNodeWrapper parent, JCRSessionWrapper session, JCRStoreProvider provider) { super(session, provider); this.objectNode = objectNode; setItem(objectNode); if (path != null) { if (path.endsWith("/") && !path.equals("/")) { path = StringUtils.substringBeforeLast(path, "/"); } try { this.localPath = objectNode.getPath(); this.localPathInProvider = objectNode.getPath(); if (path.contains(JCRSessionWrapper.DEREF_SEPARATOR)) { this.localPath = path; } // In case we are accessing versionned node, this ensure that localPath contain the expected localPath if (this.localPath.startsWith("/jcr:system")) { this.localPath = path; this.localPathInProvider = path; } } catch (RepositoryException e) { logger.error(e.getMessage(), e); } } else { try { this.localPath = objectNode.getPath(); this.localPathInProvider = objectNode.getPath(); } catch (RepositoryException e) { logger.error(e.getMessage(), e); } } if (parent != null) { parentAlreadyResolved = true; resolvedParentNode = parent; } } /** * {@inheritDoc} */ @Override public Node getRealNode() { return objectNode; } /** * {@inheritDoc} */ @Override public JCRNodeWrapper getParent() throws ItemNotFoundException, AccessDeniedException, RepositoryException { if (parentAlreadyResolved) { return resolvedParentNode; } try { if (localPath.equals("/") || localPath.equals(provider.getRelativeRoot())) { if (provider.getMountPoint().equals("/")) { throw new ItemNotFoundException(); } return (JCRNodeWrapper) session .getItem(StringUtils.substringBeforeLast(provider.getMountPoint(), "/")); } else { return (JCRNodeWrapper) session.getItem(StringUtils.substringBeforeLast(getPath(), "/")); } } catch (PathNotFoundException e) { throw new ItemNotFoundException(e); } } /** * {@inheritDoc} */ @Override public JCRUserNode getUser() { try { return (JCRUserNode) session.getUserNode(); } catch (RepositoryException e) { logger.error(e.getMessage(), e); } return null; } /** * {@inheritDoc} */ @Override public Map<String, List<String[]>> getAclEntries() { try { Map<String, List<String[]>> results = new LinkedHashMap<String, List<String[]>>(); JCRNodeWrapper n = this; try { while (true) { if (n.hasNode("j:acl")) { Node acl = n.getNode("j:acl"); NodeIterator aces = acl.getNodes(); while (aces.hasNext()) { Node ace = aces.nextNode(); if (ace.isNodeType("jnt:ace")) { String principal = ace.getProperty("j:principal").getString(); String type = ace.getProperty("j:aceType").getString(); if (!ace.hasProperty(Constants.J_ROLES)) { continue; } Value[] roles = ace.getProperty(Constants.J_ROLES).getValues(); if (!results.containsKey(principal)) { results.put(principal, new ArrayList<String[]>()); } if (!ace.isNodeType("jnt:externalAce")) { for (Value role : roles) { results.get(principal) .add(new String[] { n.getPath(), type, role.getString() }); } } else { for (Value role : roles) { results.get(principal).add( new String[] { n.getPath(), "EXTERNAL", role.getString() + "/" + ace .getProperty("j:externalPermissionsName").getString() }); } } } } if (acl.hasProperty("j:inherit") && !acl.getProperty("j:inherit").getBoolean()) { return results; } } if (n.getPath().equals("/")) { return results; } n = n.getParent(); } } catch (ItemNotFoundException e) { if (logger.isDebugEnabled()) { logger.debug(e.getMessage(), e); } } return results; } catch (RepositoryException e) { logger.error(e.getMessage(), e); } return null; } /** * {@inheritDoc} */ @Override public Map<String, Map<String, String>> getActualAclEntries() { Map<String, Map<String, String>> actualACLs = new HashMap<String, Map<String, String>>(); Map<String, List<String[]>> allACLs = getAclEntries(); if (allACLs != null) { for (Map.Entry<String, List<String[]>> entry : allACLs.entrySet()) { Map<String, String> permissionsForUser = new HashMap<String, String>(); // filtering stuff (path, GRANT/DENY, jcr:perm) for (String[] perms : entry.getValue()) { if (permissionsForUser.containsKey(perms[2])) { if (perms[0].equals(getPath())) { permissionsForUser.put(perms[2], perms[1]); } } else { permissionsForUser.put(perms[2], perms[1]); } } actualACLs.put(entry.getKey(), permissionsForUser); } } return actualACLs; } /** * {@inheritDoc} */ @Override public Map<String, List<JCRNodeWrapper>> getAvailableRoles() throws RepositoryException { Map<String, List<JCRNodeWrapper>> res = new HashMap<String, List<JCRNodeWrapper>>(); NodeIterator ni = session.getWorkspace().getQueryManager() .createQuery( "select * from [" + Constants.JAHIANT_ROLE + "] as r where isdescendantnode(r,['/roles'])", Query.JCR_SQL2) .execute().getNodes(); while (ni.hasNext()) { JCRNodeWrapper role = (JCRNodeWrapper) ni.nextNode(); boolean add = false; if (role.hasProperty("j:hidden") && role.getProperty("j:hidden").getBoolean()) { // skip } else if (role.hasProperty("j:nodeTypes")) { Value[] values = role.getProperty("j:nodeTypes").getValues(); if (values.length > 0) { for (Value value : values) { if (isNodeType(value.getString())) { add = true; break; } } } else { add = true; } } else { add = true; } if (add) { String roleGroup; if (role.hasProperty("j:roleGroup")) { roleGroup = role.getProperty("j:roleGroup").getString(); } else { roleGroup = "default"; } if (!res.containsKey(roleGroup)) { res.put(roleGroup, new ArrayList<JCRNodeWrapper>()); } res.get(roleGroup).add(role); } } return res; } /** * {@inheritDoc} */ @Override public boolean hasPermission(String perm) { try { if (session.isSystem()) { return true; } AccessControlManager accessControlManager = getAccessControlManager(); return accessControlManager == null || accessControlManager.hasPrivileges(localPathInProvider, new Privilege[] { accessControlManager.privilegeFromName(perm) }); } catch (RepositoryException re) { logger.error("Cannot check permission " + perm, re); return false; } } @Override public AccessControlManager getAccessControlManager() throws RepositoryException { Session providerSession = session.getProviderSession(provider); // this is not a Jackrabbit implementation, we will use the new JCR 2.0 API instead. AccessControlManager accessControlManager = null; try { accessControlManager = providerSession.getAccessControlManager(); } catch (UnsupportedRepositoryOperationException uroe) { logger.warn("Access control manager is not supported for node " + getPath() + ": " + uroe.getMessage()); } return accessControlManager; } @Override public Set<String> getPermissions() { Set<String> result = new HashSet<String>(); try { AccessControlManager accessControlManager = getAccessControlManager(); if (accessControlManager != null) { Privilege[] p = accessControlManager.getPrivileges(localPathInProvider); for (Privilege privilege : p) { result.add(privilege.getName()); if (privilege.isAggregate()) { for (Privilege privilege1 : privilege.getAggregatePrivileges()) { result.add(privilege1.getName()); } } } } } catch (RepositoryException re) { logger.error("Cannot check perm ", re); } return result; } @Override public BitSet getPermissionsAsBitSet() { BitSet b = null; try { AccessControlManager accessControlManager = getAccessControlManager(); if (accessControlManager == null) { return b; } Privilege[] app = accessControlManager.getPrivileges(localPathInProvider); Privilege[] pr = accessControlManager.getSupportedPrivileges(localPathInProvider); b = new BitSet(pr.length); if (app.length == pr.length) { // in case of admin user all supported permissions are present b.set(0, pr.length); } else { Set<Privilege> effective = new HashSet<Privilege>(); for (Privilege privilege : app) { effective.add(privilege); if (privilege.isAggregate()) { for (Privilege p : privilege.getAggregatePrivileges()) { effective.add(p); } } } int position = 0; for (Privilege privilege : pr) { if (effective.contains(privilege)) { b.set(position); } position++; } } } catch (RepositoryException e) { logger.error("Cannot check perm ", e); } return b; } /** * {@inheritDoc} */ @Override public boolean grantRoles(String principalKey, Set<String> roles) throws RepositoryException { Map<String, String> m = new HashMap<String, String>(); for (String role : roles) { m.put(role, "GRANT"); } return changeRoles(principalKey, m); } /** * {@inheritDoc} */ @Override public boolean denyRoles(String principalKey, Set<String> roles) throws RepositoryException { Map<String, String> m = new HashMap<String, String>(); for (String role : roles) { m.put(role, "DENY"); } return changeRoles(principalKey, m); } /** * {@inheritDoc} */ @Override public boolean changeRoles(String principalKey, Map<String, String> roles) throws RepositoryException { if (!isCheckedOut() && isNodeType(Constants.MIX_VERSIONABLE)) { getSession().checkout(this); } List<String> gr = new ArrayList<String>(); List<String> den = new ArrayList<String>(); List<String> rem = new ArrayList<String>(); for (Map.Entry<String, String> entry : roles.entrySet()) { if ("GRANT".equals(entry.getValue())) { gr.add(entry.getKey()); } else if ("DENY".equals(entry.getValue())) { den.add(entry.getKey()); } else if ("REMOVE".equals(entry.getValue())) { rem.add(entry.getKey()); } } Node acl = getOrCreateAcl(); NodeIterator ni = acl.getNodes(); Node aceg = null; Node aced = null; while (ni.hasNext()) { Node ace = ni.nextNode(); if (ace.isNodeType("jnt:ace") && !ace.isNodeType("jnt:externalAce")) { if (ace.getProperty("j:principal").getString().equals(principalKey)) { if (ace.getProperty("j:aceType").getString().equals("GRANT")) { aceg = ace; } else { aced = ace; } } } } if (aceg == null) { aceg = acl.addNode("GRANT_" + JCRContentUtils.replaceColon(principalKey).replaceAll("/", "_"), "jnt:ace"); aceg.setProperty("j:principal", principalKey); aceg.setProperty("j:protected", false); aceg.setProperty("j:aceType", "GRANT"); } if (aced == null) { aced = acl.addNode("DENY_" + JCRContentUtils.replaceColon(principalKey).replaceAll("/", "_"), "jnt:ace"); aced.setProperty("j:principal", principalKey); aced.setProperty("j:protected", false); aced.setProperty("j:aceType", "DENY"); } List<String> grClone = new ArrayList<String>(gr); List<String> denClone = new ArrayList<String>(den); if (aceg.hasProperty(Constants.J_ROLES)) { final Value[] values = aceg.getProperty(Constants.J_ROLES).getValues(); for (Value value : values) { final String s = value.getString(); if (!gr.contains(s) && !den.contains(s) && !rem.contains(s)) { grClone.add(s); } } } if (aced.hasProperty(Constants.J_ROLES)) { final Value[] values = aced.getProperty(Constants.J_ROLES).getValues(); for (Value value : values) { final String s = value.getString(); if (!gr.contains(s) && !den.contains(s) && !rem.contains(s)) { denClone.add(s); } } } String[] grs = new String[grClone.size()]; grClone.toArray(grs); if (grs.length == 0) { aceg.remove(); } else { aceg.setProperty(Constants.J_ROLES, grs); } String[] dens = new String[denClone.size()]; denClone.toArray(dens); if (dens.length == 0) { aced.remove(); } else { aced.setProperty(Constants.J_ROLES, dens); } return true; } /** * {@inheritDoc} */ @Override public boolean revokeRolesForPrincipal(String principalKey) throws RepositoryException { Node acl = getOrCreateAcl(); NodeIterator ni = acl.getNodes(); while (ni.hasNext()) { Node ace = ni.nextNode(); if (ace.isNodeType("jnt:ace") && !ace.isNodeType("jnt:externalAce")) { if (ace.getProperty("j:principal").getString().equals(principalKey)) { ace.remove(); } } } return true; } /** * {@inheritDoc} */ @Override public boolean revokeAllRoles() throws RepositoryException { if (hasNode("j:acl")) { JCRNodeWrapper acl = getNode("j:acl"); NodeIterator ni = acl.getNodes(); while (ni.hasNext()) { Node ace = ni.nextNode(); if (ace.isNodeType("jnt:ace") && !ace.isNodeType("jnt:externalAce")) { ace.remove(); } } if (!acl.hasNodes()) { acl.remove(); if (isNodeType("jmix:accessControlled")) { removeMixin("jmix:accessControlled"); } } return true; } return false; } /** * {@inheritDoc} */ @Override public boolean setAclInheritanceBreak(boolean breakAclInheritance) throws RepositoryException { try { boolean inheritAcl = !breakAclInheritance; Node aclNode = getOrCreateAcl(); if (!aclNode.hasProperty("j:inherit") || aclNode.getProperty("j:inherit").getBoolean() != inheritAcl) { aclNode.setProperty("j:inherit", inheritAcl); } } catch (RepositoryException e) { logger.error("Cannot change acl", e); return false; } return true; } /** * Check if acl inheritance is broken on the given node or not * * @return true if ACL inheritance is broken * @throws RepositoryException */ @Override public boolean getAclInheritanceBreak() throws RepositoryException { if (hasNode("j:acl")) { Node acl = getNode("j:acl"); return acl.hasProperty("j:inherit") && !acl.getProperty("j:inherit").getBoolean(); } return false; } /** * Returns the ACL node of the given node or creates one * * @return the ACL <code>Node</code> for the given node * @throws RepositoryException */ public Node getOrCreateAcl() throws RepositoryException { if (hasNode("j:acl")) { return getNode("j:acl"); } else { if (!isCheckedOut()) { checkout(); } addMixin("jmix:accessControlled"); return addNode("j:acl", "jnt:acl"); } } /** * {@inheritDoc} */ @Override public JCRNodeWrapper createCollection(String name) throws RepositoryException { return addNode(name, Constants.JAHIANT_FOLDER); } /** * {@inheritDoc} */ @Override public JCRNodeWrapper uploadFile(String name, final InputStream is, final String contentType) throws RepositoryException { checkLock(); name = JCRContentUtils.escapeLocalNodeName(FilenameUtils.getName(name)); JCRNodeWrapper file = null; try { file = getNode(name); if (!file.isCheckedOut()) { file.getSession().checkout(file); } } catch (PathNotFoundException e) { if (logger.isDebugEnabled()) { logger.debug("file " + name + " does not exist, creating..."); } if (!isCheckedOut()) { getSession().checkout(this); } file = addNode(name, Constants.JAHIANT_FILE); } if (file != null) { file.getFileContent().uploadFile(is, contentType); } else { logger.error("can't write to file " + name + " because it doesn't exist"); } return file; } /** * {@inheritDoc} */ @Override public JCRNodeWrapper addNode(String name) throws RepositoryException { checkLock(); Node n = objectNode.addNode(name); return provider.getNodeWrapper(n, buildSubnodePath(name), this, session); } protected String buildSubnodePath(String name) { if (localPath.equals("/")) { return localPath + name; } else { return localPath + "/" + name; } } /** * {@inheritDoc} */ @Override public JCRNodeWrapper addNode(String name, String type) throws RepositoryException { checkLock(); Node n = objectNode.addNode(name, type); JCRNodeWrapper newNode = provider.getNodeWrapper(n, buildSubnodePath(name), this, session); session.registerNewNode(newNode); return newNode; } @Override public JCRNodeWrapper addNode(String name, String type, String identifier, Calendar created, String createdBy, Calendar lastModified, String lastModifiedBy) throws ItemExistsException, PathNotFoundException, NoSuchNodeTypeException, LockException, VersionException, ConstraintViolationException, RepositoryException { checkLock(); if (objectNode instanceof NodeImpl) { JahiaSessionImpl jrSession = (JahiaSessionImpl) objectNode.getSession(); jrSession.getNodeTypeInstanceHandler().setCreated(created); jrSession.getNodeTypeInstanceHandler().setCreatedBy(createdBy); jrSession.getNodeTypeInstanceHandler().setLastModified(lastModified); jrSession.getNodeTypeInstanceHandler().setLastModifiedBy(lastModifiedBy); try { if (identifier != null) { org.jahia.services.content.nodetypes.Name jahiaName = new org.jahia.services.content.nodetypes.Name( name, NodeTypeRegistry.getInstance().getNamespaces()); Name qname = NameFactoryImpl.getInstance() .create(jahiaName.getUri() == null ? "" : jahiaName.getUri(), jahiaName.getLocalName()); Name typeName = null; if (type != null) { org.jahia.services.content.nodetypes.Name jahiaTypeName = NodeTypeRegistry.getInstance() .getNodeType(type).getNameObject(); typeName = NameFactoryImpl.getInstance().create(jahiaTypeName.getUri(), jahiaTypeName.getLocalName()); } Node child = ((NodeImpl) objectNode).addNode(qname, typeName, org.apache.jackrabbit.core.id.NodeId.valueOf(identifier)); return provider.getNodeWrapper(child, buildSubnodePath(name), this, session); } else { return addNode(name, type); } } finally { jrSession.getNodeTypeInstanceHandler().setCreated(null); jrSession.getNodeTypeInstanceHandler().setCreatedBy(null); jrSession.getNodeTypeInstanceHandler().setLastModified(null); jrSession.getNodeTypeInstanceHandler().setLastModifiedBy(null); } } else { return addNode(name, type); } } /** * {@inheritDoc} */ @Override public JCRPlaceholderNode getPlaceholder() throws RepositoryException { return new JCRPlaceholderNode(this); } /** * {@inheritDoc} * * @deprecated As of JCR 2.0, {@link #getIdentifier()} should be used instead. */ @Override public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException { return objectNode.getUUID(); } /** * {@inheritDoc} */ @Override public String getAbsoluteUrl(ServletRequest request) { return provider.getAbsoluteContextPath(request) + getUrl(); } /** * {@inheritDoc} */ @Override public String getUrl() { try { if (isNodeType(Constants.NT_FILE)) { return provider.getHttpPath() + "/" + getSession().getWorkspace().getName() + Text.escapePath(getCanonicalPath()); } else { String path = JCRSessionFactory.getInstance().getCurrentServletPath(); if (path == null) { path = "/cms/render"; } return Jahia.getContextPath() + path + "/" + getSession().getWorkspace().getName() + "/" + getSession().getLocale() + Text.escapePath(getPath()) + ".html"; } } catch (RepositoryException e) { logger.error("Cannot get type", e); return null; } } /** * {@inheritDoc} */ @Override public String getAbsoluteWebdavUrl(final HttpServletRequest request) { return provider.getAbsoluteContextPath(request) + getWebdavUrl(); } /** * {@inheritDoc} */ @Override public String getWebdavUrl() { return Jahia.getContextPath() + provider.getWebdavPath() + "/" + session.getWorkspace().getName() + localPathInProvider; } /** * {@inheritDoc} */ @Override public List<String> getThumbnails() { List<String> names = new ArrayList<String>(); try { NodeIterator ni = objectNode.getNodes(); while (ni.hasNext()) { Node node = ni.nextNode(); String name = node.getName(); if (!name.equals("jcr:content") && (node.isNodeType("jnt:resource") || (node.isNodeType("nt:frozenNode") && "jnt:resource".equals(node.getProperty("jcr:frozenPrimaryType").getString())))) { names.add(name); } } } catch (RepositoryException e) { logger.error(e.getMessage(), e); } return names; } /** * {@inheritDoc} */ @Override public String getThumbnailUrl(String name) { String url = getUrl(); return url + (url.indexOf('?') != -1 ? "&" : "?") + "t=" + name; } /** * {@inheritDoc} */ @Override public Map<String, String> getThumbnailUrls() { List<String> list = getThumbnails(); Map<String, String> map = new HashMap<String, String>(list.size()); for (String thumbnailName : list) { map.put(thumbnailName, getThumbnailUrl(thumbnailName)); } return map; } /** * {@inheritDoc} */ @Override public JCRNodeWrapper getNode(String s) throws PathNotFoundException, RepositoryException { if (objectNode.hasNode(s)) { // we have that node in our default JR repository final Node node = objectNode.getNode(s); // check if this node is not a dynamic mount point to an external repository if (!isExternalRoot(node)) { // the node is local -> get it return s.indexOf('/') == -1 ? provider.getNodeWrapper(node, buildSubnodePath(s), this, session) : provider.getNodeWrapper(node, session); } } // no local node found -> check mounted providers if (provider.getService() != null && provider.getSessionFactory().areMultipleMountPointsRegistered()) { String targetPath = getPath() + '/' + s; JCRStoreProvider targetProvider = provider.getSessionFactory().getProvider(targetPath, false); if (targetProvider != null && targetProvider != provider) { // we found a provider which can handle the specified path JCRNodeWrapper providerRoot = targetProvider.getNodeWrapper(session .getProviderSession(targetProvider).getNode(targetProvider.getRelativeRoot().isEmpty() ? "/" : targetProvider.getRelativeRoot()), "/", this, session); if (!targetProvider.getMountPoint().equals(targetPath)) { String childPath = StringUtils.substringAfter(targetPath, targetProvider.getMountPoint() + "/"); if (childPath.length() > 0) { return providerRoot.getNode(childPath); } } return providerRoot; } } throw new PathNotFoundException(s); } private boolean isExternalRoot(final Node node) throws RepositoryException, ValueFormatException, PathNotFoundException { return node.hasProperty("j:isExternalProviderRoot") && node.getProperty("j:isExternalProviderRoot").getBoolean() || provider.getSessionFactory().getMountPoints().containsKey(node.getPath()); } /** * {@inheritDoc} */ @Override public JCRNodeIteratorWrapper getNodes() throws RepositoryException { return getNodes(null, null); } /** * {@inheritDoc} */ @Override public JCRNodeIteratorWrapper getNodes(String namePattern) throws RepositoryException { return getNodes(namePattern, null); } /** * {@inheritDoc} */ @Override public JCRNodeIteratorWrapper getNodes(String[] nameGlobs) throws RepositoryException { return getNodes(null, nameGlobs); } protected JCRNodeIteratorWrapper getNodes(String namePattern, String[] nameGlobs) throws RepositoryException { List<JCRNodeWrapper> list = null; List<String> mounts = null; if (provider.getService() != null && provider.getSessionFactory().areMultipleMountPointsRegistered()) { Map<String, JCRStoreProvider> mountPoints = provider.getSessionFactory().getMountPoints(); // if we have any registered mount points (except the default one, which is "/") String path = getPath(); for (Map.Entry<String, JCRStoreProvider> entry : mountPoints.entrySet()) { String key = entry.getKey(); // skip default provider and those, whose mount point path does not start with the path of this node if (!entry.getValue().isDefault() && key.startsWith(path)) { int pos = key.lastIndexOf('/'); String mpp = pos > 0 ? key.substring(0, pos) : "/"; if (mpp.equals(path)) { // mount point matches the path; check name patterns if they were specified final String name = key.substring(pos + 1); if (namePattern == null && nameGlobs == null || key.length() > pos + 1 && (namePattern != null && ChildrenCollectorFilter.matches(name, namePattern) || nameGlobs != null && ChildrenCollectorFilter.matches(name, nameGlobs))) { JCRStoreProvider storeProvider = entry.getValue(); String root = storeProvider.getRelativeRoot(); try { final Node node = session.getProviderSession(storeProvider) .getNode(root.length() == 0 ? "/" : root); if (list == null) { list = new LinkedList<JCRNodeWrapper>(); mounts = new LinkedList<String>(); } list.add(storeProvider.getNodeWrapper(node, "/", this, session)); mounts.add(name); } catch (PathNotFoundException e) { // current session doesn't have the right to read the mounted node } } } } } } NodeIterator ni = namePattern != null ? objectNode.getNodes(namePattern) : (nameGlobs != null ? objectNode.getNodes(nameGlobs) : objectNode.getNodes()); return new ChildNodesIterator(ni, mounts, list, this, session, provider); } @Override public Map<String, String> getPropertiesAsString() throws RepositoryException { Map<String, String> res = new HashMap<String, String>(); PropertyIterator pi = getProperties(); if (pi != null) { while (pi.hasNext()) { Property p = pi.nextProperty(); if (p.getType() == PropertyType.BINARY) { continue; } if (!p.isMultiple()) { res.put(p.getName(), p.getString()); } else { Value[] vs = p.getValues(); StringBuilder b = new StringBuilder(); for (int i = 0; i < vs.length; i++) { Value v = vs[i]; b.append(v.getString()); if (i + 1 < vs.length) { b.append(" "); } } res.put(p.getName(), b.toString()); } } } return res; } /** * {@inheritDoc} */ @Override public String getName() { try { if ((localPathInProvider.equals("/") || localPathInProvider.equals(provider.getRelativeRoot())) && provider.getMountPoint().length() > 1) { String mp = provider.getMountPoint(); return mp.substring(mp.lastIndexOf('/') + 1); } else { return objectNode.getName(); } } catch (RepositoryException e) { logger.error("Repository error unable to read node {}", localPath, e); } return null; } /** * {@inheritDoc} */ @Override public ExtendedNodeType getPrimaryNodeType() throws RepositoryException { try { return NodeTypeRegistry.getInstance().getNodeType(objectNode.getPrimaryNodeType().getName()); } catch (NoSuchNodeTypeException e) { return NodeTypeRegistry.getInstance().getNodeType("nt:base"); } } /** * {@inheritDoc} */ @Override public String getPrimaryNodeTypeName() throws RepositoryException { String name = objectNode.getPrimaryNodeType().getName(); if (NodeTypeRegistry.getInstance().hasNodeType(name)) { return name; } else { return "nt:base"; } } /** * {@inheritDoc} */ @Override public ExtendedNodeType[] getMixinNodeTypes() throws RepositoryException { List<NodeType> l = null; NodeType[] mixinNodeTypes = objectNode.getMixinNodeTypes(); for (NodeType nodeType : mixinNodeTypes) { try { if (l == null) { l = new ArrayList<NodeType>(mixinNodeTypes.length); } l.add(NodeTypeRegistry.getInstance().getNodeType(nodeType.getName())); } catch (NoSuchNodeTypeException e) { logger.debug("Skipping missing mixin {}", nodeType.getName()); } } return l != null ? l.toArray(new ExtendedNodeType[l.size()]) : EMPTY_EXTENDED_NODE_TYPE_ARRAY; } /** * {@inheritDoc} */ @Override public void addMixin(String s) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException { checkLock(); try { objectNode.addMixin(s); } finally { applicablePropertyDefinition.clear(); hasPropertyCache.clear(); } } /** * {@inheritDoc} */ @Override public void removeMixin(String mixinName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException { checkLock(); try { objectNode.removeMixin(mixinName); // Removing a mixin also causes removal of any child nodes defined by the mixin. // In case the mixin defines any child nodes, flush the cache to ensure it does not contain any nodes // that has been just removed along with the mixin. if (!NodeTypeRegistry.getInstance().getNodeType(mixinName).getChildNodeDefinitionsAsMap().isEmpty()) { getSession().flushCaches(); } } finally { applicablePropertyDefinition.clear(); hasPropertyCache.clear(); } } /** * {@inheritDoc} */ @Override public boolean canAddMixin(String s) throws NoSuchNodeTypeException, RepositoryException { try { checkLock(); } catch (LockException e) { return false; } return objectNode.canAddMixin(s); } /** * {@inheritDoc} */ @Override public ExtendedNodeDefinition getDefinition() throws RepositoryException { NodeDefinition definition = objectNode.getDefinition(); if (definition == null) { return null; } ExtendedNodeType nt = NodeTypeRegistry.getInstance() .getNodeType(definition.getDeclaringNodeType().getName()); if (definition.getName().equals("*")) { for (ExtendedNodeDefinition d : nt.getUnstructuredChildNodeDefinitions().values()) { ExtendedNodeType[] requiredPrimaryTypes = d.getRequiredPrimaryTypes(); NodeType[] a2 = definition.getRequiredPrimaryTypes(); boolean valid = true; for (ExtendedNodeType extendedNodeType : requiredPrimaryTypes) { boolean found = false; for (NodeType nodeType : a2) { if (nodeType.getName().equals(extendedNodeType.getName())) { found = true; break; } } if (!found) { valid = false; break; } } if (valid) return d; } } else { return nt.getNodeDefinition(definition.getName()); } return null; } /** * {@inheritDoc} */ @Override public List<String> getNodeTypes() throws RepositoryException { List<String> results = new ArrayList<String>(); if (NodeTypeRegistry.getInstance().hasNodeType(objectNode.getPrimaryNodeType().getName())) { results.add(objectNode.getPrimaryNodeType().getName()); } else { results.add("nt:base"); } NodeType[] mixin = objectNode.getMixinNodeTypes(); for (int i = 0; i < mixin.length; i++) { NodeType mixinType = mixin[i]; if (NodeTypeRegistry.getInstance().hasNodeType(mixinType.getName())) { results.add(mixinType.getName()); } } return results; } /** * {@inheritDoc} */ @Override public boolean isNodeType(String type) throws RepositoryException { return "nt:base".equals(type) || objectNode.isNodeType(type); } /** * {@inheritDoc} */ @Override public boolean isCollection() { return true; // try { // return objectNode.isNodeType("jmix:collection") || objectNode.isNodeType("nt:folder") || objectNode.getPath().equals("/"); // } catch (RepositoryException e) { // return false; // } } /** * {@inheritDoc} */ @Override public boolean isFile() { try { return isNodeType(Constants.NT_FILE); } catch (RepositoryException e) { logger.error("Cannot get type", e); return false; } } /** * {@inheritDoc} */ @Override public boolean isPortlet() { try { return isNodeType(Constants.JAHIANT_PORTLET); } catch (RepositoryException e) { logger.error("Cannot get type", e); return false; } } /** * {@inheritDoc} */ @Override public Date getLastModifiedAsDate() { try { return objectNode.getProperty(Constants.JCR_LASTMODIFIED).getDate().getTime(); } catch (Exception e) { } return null; } /** * {@inheritDoc} */ @Override public Date getContentLastModifiedAsDate() { try { Node content = objectNode.getNode(Constants.JCR_CONTENT); return content.getProperty(Constants.JCR_LASTMODIFIED).getDate().getTime(); } catch (PathNotFoundException pnfe) { } catch (RepositoryException e) { } return null; } /** * {@inheritDoc} */ @Override public Date getLastPublishedAsDate() { try { return objectNode.getProperty(Constants.LASTPUBLISHED).getDate().getTime(); } catch (Exception e) { } return null; } /** * {@inheritDoc} */ @Override public Date getContentLastPublishedAsDate() { try { Node content = objectNode.getNode(Constants.JCR_CONTENT); return content.getProperty(Constants.LASTPUBLISHED).getDate().getTime(); } catch (PathNotFoundException pnfe) { } catch (RepositoryException e) { } return null; } /** * {@inheritDoc} */ @Override public Date getCreationDateAsDate() { try { return objectNode.getProperty(Constants.JCR_CREATED).getDate().getTime(); } catch (RepositoryException e) { } return null; } /** * {@inheritDoc} */ @Override public String getCreationUser() { try { return objectNode.getProperty(Constants.JCR_CREATEDBY).getString(); } catch (RepositoryException e) { } return null; } /** * {@inheritDoc} */ @Override public String getModificationUser() { try { return objectNode.getProperty(Constants.JCR_LASTMODIFIEDBY).getString(); } catch (RepositoryException e) { } return null; } /** * {@inheritDoc} */ @Override public String getPublicationUser() { try { return objectNode.getProperty(Constants.LASTPUBLISHEDBY).getString(); } catch (RepositoryException e) { } return null; } /** * {@inheritDoc} */ @Override public String getLanguage() { String language = null; try { if (Constants.JAHIANT_TRANSLATION.equals(getPrimaryNodeTypeName())) { language = getProperty("jcr:language").getString(); } } catch (RepositoryException e1) { } if (language == null && getSession().getLocale() != null) { try { language = getI18N(getSession().getLocale()).getProperty("jcr:language").getString(); } catch (Exception e) { language = getSession().getLocale().toString(); } } return language; } @Override public List<Locale> getExistingLocales() throws RepositoryException { List<Locale> r = new ArrayList<Locale>(); NodeIterator ni = getI18Ns(); while (ni.hasNext()) { Node n = ni.nextNode(); r.add(LanguageCodeConverters.languageCodeToLocale(n.getProperty("jcr:language").getString())); } return r; } /** * Return the internationalization node, containing localized properties * * @param locale * @return */ @Override public Node getI18N(Locale locale) throws RepositoryException { return getI18N(locale, true); } @Override public boolean hasI18N(Locale locale) throws RepositoryException { return hasI18N(locale, true); } @Override public boolean hasI18N(Locale locale, boolean fallback) throws RepositoryException { return hasI18N(locale, fallback, true); } private boolean hasI18N(Locale locale, boolean fallback, boolean checkPublication) throws RepositoryException { boolean b = checkI18NNode(locale, checkPublication); if (!b && fallback) { final Locale fallbackLocale = getSession().getFallbackLocale(); if (fallbackLocale != null && fallbackLocale != locale) { b = checkI18NNode(fallbackLocale, checkPublication); } } return b; } private boolean checkI18NNode(Locale locale, boolean checkPublication) throws RepositoryException { boolean b = false; final String transName = getTranslationNodeName(locale); if ((i18NobjectNodes != null && i18NobjectNodes.containsKey(locale)) || objectNode.hasNode(transName)) { if (checkPublication && Constants.LIVE_WORKSPACE.equals(session.getWorkspace().getName())) { final Node node = objectNode.getNode(transName); b = !node.hasProperty(Constants.PUBLISHED) || node.getProperty(Constants.PUBLISHED).getBoolean(); } else { b = true; } } return b; } private String getTranslationNodeName(Locale locale) { return TRANSLATION_PREFIX + locale; } @Override public Node getI18N(Locale locale, boolean fallback) throws RepositoryException { //getSession().getLocale() if (i18NobjectNodes == null) { i18NobjectNodes = new HashMap<Locale, Node>(); } Node node; if (i18NobjectNodes.containsKey(locale)) { node = i18NobjectNodes.get(locale); if (node != null) { return node; } } else { final String translationNodeName = getTranslationNodeName(locale); if (objectNode.hasNode(translationNodeName)) { node = objectNode.getNode(translationNodeName); if (!Constants.LIVE_WORKSPACE.equals(session.getWorkspace().getName()) || !node.hasProperty(Constants.PUBLISHED) || node.getProperty(Constants.PUBLISHED).getBoolean()) { i18NobjectNodes.put(locale, node); return node; } } } if (fallback) { final Locale fallbackLocale = getSession().getFallbackLocale(); if (fallbackLocale != null && fallbackLocale != locale) { return getI18N(fallbackLocale); } } throw new ItemNotFoundException(locale.toString()); } @Override public NodeIterator getI18Ns() throws RepositoryException { return objectNode.getNodes("j:translation*"); } @Override public Node getOrCreateI18N(final Locale locale) throws RepositoryException { try { return getI18N(locale, false); } catch (RepositoryException e) { Node t = objectNode.addNode(getTranslationNodeName(locale), Constants.JAHIANT_TRANSLATION); t.setProperty("jcr:language", locale.toString()); i18NobjectNodes.put(locale, t); return t; } } @Override public Node getOrCreateI18N(Locale locale, Calendar created, String createdBy, Calendar lastModified, String lastModifiedBy) throws RepositoryException { JahiaSessionImpl jrSession = (JahiaSessionImpl) objectNode.getSession(); try { return getI18N(locale, false); } catch (RepositoryException e) { try { jrSession.getNodeTypeInstanceHandler().setCreated(created); jrSession.getNodeTypeInstanceHandler().setCreatedBy(createdBy); jrSession.getNodeTypeInstanceHandler().setLastModified(lastModified); jrSession.getNodeTypeInstanceHandler().setLastModifiedBy(lastModifiedBy); Node t = objectNode.addNode(getTranslationNodeName(locale), Constants.JAHIANT_TRANSLATION); t.setProperty("jcr:language", locale.toString()); i18NobjectNodes.put(locale, t); return t; } finally { jrSession.getNodeTypeInstanceHandler().setCreated(null); jrSession.getNodeTypeInstanceHandler().setCreatedBy(null); jrSession.getNodeTypeInstanceHandler().setLastModified(null); jrSession.getNodeTypeInstanceHandler().setLastModifiedBy(null); } } } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper getProperty(String name) throws javax.jcr.PathNotFoundException, javax.jcr.RepositoryException { ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(name); if (epd != null) { return internalGetProperty(name, epd); } else { throw new PathNotFoundException(name); } } private JCRPropertyWrapper internalGetProperty(String name, ExtendedPropertyDefinition epd) throws RepositoryException { final Locale locale = getSession().getLocale(); if (epd == null) { epd = getApplicablePropertyDefinition(name); } if (epd == null) { throw new PathNotFoundException(name); } if (locale != null) { if (epd.isInternationalized()) { Pattern pathPattern = JCRContentUtils.getInstance().getHandleFallbackLocaleForPathPattern(); if (pathPattern == null || locale.equals(SettingsBean.getInstance().getDefaultLocale())) { try { final Node localizedNode = getI18N(locale); return new JCRPropertyWrapperImpl(this, localizedNode.getProperty(name), session, provider, epd, name); } catch (ItemNotFoundException e) { return new JCRPropertyWrapperImpl(this, objectNode.getProperty(name), session, provider, epd); } } else { return internalGetPropertyI18nWithDefFallback(name, epd, locale, pathPattern); } } } return new JCRPropertyWrapperImpl(this, objectNode.getProperty(name), session, provider, epd); } private JCRPropertyWrapper internalGetPropertyI18nWithDefFallback(String name, ExtendedPropertyDefinition epd, Locale locale, Pattern pathPattern) throws RepositoryException { Node localizedNode = null; try { localizedNode = getI18N(locale); return new JCRPropertyWrapperImpl(this, localizedNode.getProperty(name), session, provider, epd, name); } catch (ItemNotFoundException e) { try { return new JCRPropertyWrapperImpl(this, objectNode.getProperty(name), session, provider, epd); } catch (PathNotFoundException pnte) { if (pathPattern.matcher(getPath()).matches()) { try { localizedNode = getI18N(SettingsBean.getInstance().getDefaultLocale()); return new JCRPropertyWrapperImpl(this, localizedNode.getProperty(name), session, provider, epd, name); } catch (ItemNotFoundException e2) { throw pnte; } } throw pnte; } } catch (PathNotFoundException e) { if (pathPattern.matcher(getPath()).matches()) { try { localizedNode = getI18N(SettingsBean.getInstance().getDefaultLocale()); return new JCRPropertyWrapperImpl(this, localizedNode.getProperty(name), session, provider, epd, name); } catch (ItemNotFoundException e2) { throw e; } } throw e; } } /** * {@inheritDoc} */ @Override public PropertyIterator getProperties() throws RepositoryException { final Locale locale = getSession().getLocale(); if (locale != null) { return new LazyPropertyIterator(this, locale); } return new LazyPropertyIterator(this, null); } /** * {@inheritDoc} */ @Override public PropertyIterator getProperties(String s) throws RepositoryException { final Locale locale = getSession().getLocale(); if (locale != null) { return new LazyPropertyIterator(this, locale, s); } return new LazyPropertyIterator(this, null, s); } /** * {@inheritDoc} */ @Override public String getPropertyAsString(String name) { try { Property property = getProperty(name); if (property == null) { return null; } if (property.getType() == PropertyType.BINARY) { return null; } if (!property.isMultiple()) { return property.getString(); } else { Value[] vs = property.getValues(); StringBuilder b = new StringBuilder(); for (int i = 0; i < vs.length; i++) { Value v = vs[i]; b.append(v.getString()); if (i + 1 < vs.length) { b.append(" "); } } return b.toString(); } } catch (RepositoryException e) { return null; } } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, String value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { Value v = null; if (value != null) { v = getSession().getValueFactory().createValue(value); } return setProperty(name, v); } private String ensurePrefixedName(String name) { if (!name.startsWith("{")) { return name; } org.jahia.services.content.nodetypes.Name nameObj = new org.jahia.services.content.nodetypes.Name(name, NodeTypeRegistry.getInstance().getNamespaces()); return StringUtils.isEmpty(nameObj.getPrefix()) ? nameObj.getLocalName() : nameObj.getPrefix() + ":" + nameObj.getLocalName(); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, Value value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { hasPropertyCache.remove(name); name = ensurePrefixedName(name); final Locale locale = getSession().getLocale(); ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(name); if (epd == null) { throw new ConstraintViolationException("Couldn't find definition for property " + name); } if (value != null && PropertyType.UNDEFINED != epd.getRequiredType() && value.getType() != epd.getRequiredType() && !(value.getType() == PropertyType.WEAKREFERENCE && epd.getRequiredType() == PropertyType.REFERENCE)) { // if the type doesn't match the required type, we attempt a conversion. value = getSession().getValueFactory().createValue(value.getString(), epd.getRequiredType()); } if (!session.isSystem() && !epd.isProtected() && !epd.getDeclaringNodeType().canSetProperty(epd.getName(), value)) { throw new ConstraintViolationException("Invalid value for : " + epd.getName()); } checkLock(); value = JCRStoreService.getInstance().getInterceptorChain().beforeSetValue(this, name, epd, value); if (locale != null) { if (epd.isInternationalized()) { return new JCRPropertyWrapperImpl(this, getOrCreateI18N(locale).setProperty(name, value), session, provider, epd, name); } } if (value == null) { objectNode.setProperty(name, (Value) null); return new JCRPropertyWrapperImpl(this, null, session, provider, epd); } return new JCRPropertyWrapperImpl(this, objectNode.setProperty(name, value), session, provider, epd); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, Value value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { hasPropertyCache.remove(name); final Locale locale = getSession().getLocale(); name = ensurePrefixedName(name); ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(name); if (epd == null) { throw new ConstraintViolationException("Couldn't find definition for property " + name); } if (!session.isSystem() && !epd.isProtected() && !epd.getDeclaringNodeType().canSetProperty(epd.getName(), value)) { throw new ConstraintViolationException("Invalid value for : " + epd.getName()); } checkLock(); value = JCRStoreService.getInstance().getInterceptorChain().beforeSetValue(this, name, epd, value); if (locale != null) { if (epd.isInternationalized()) { return new JCRPropertyWrapperImpl(this, getOrCreateI18N(locale).setProperty(name, value, type), session, provider, epd, name); } } if (value == null) { objectNode.setProperty(name, (Value) null); return new JCRPropertyWrapperImpl(this, null, session, provider, epd); } return new JCRPropertyWrapperImpl(this, objectNode.setProperty(name, value, type), session, provider, epd); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, Value[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { hasPropertyCache.remove(name); final Locale locale = getSession().getLocale(); name = ensurePrefixedName(name); ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(name); if (epd == null) { throw new ConstraintViolationException("Couldn't find definition for property " + name); } if (values != null) { Value[] valuesCopy = new Value[values.length]; for (int i = 0; i < values.length; i++) { if (values[i] != null && PropertyType.UNDEFINED != epd.getRequiredType() && values[i].getType() != epd.getRequiredType() && !(values[i].getType() == PropertyType.WEAKREFERENCE && epd.getRequiredType() == PropertyType.REFERENCE)) { valuesCopy[i] = getSession().getValueFactory().createValue(values[i].getString(), epd.getRequiredType()); } else { valuesCopy[i] = values[i]; } } values = valuesCopy; } if (!session.isSystem() && !epd.isProtected() && !epd.getDeclaringNodeType().canSetProperty(epd.getName(), values)) { throw new ConstraintViolationException("Invalid value for : " + epd.getName()); } checkLock(); values = JCRStoreService.getInstance().getInterceptorChain().beforeSetValues(this, name, epd, values); if (locale != null) { if (epd.isInternationalized()) { if (values == null) { getOrCreateI18N(locale).setProperty(name, (Value[]) null); return new JCRPropertyWrapperImpl(this, null, session, provider, epd); } return new JCRPropertyWrapperImpl(this, getOrCreateI18N(locale).setProperty(name, values), session, provider, epd, name); } } if (values == null) { objectNode.setProperty(name, (Value[]) null); return new JCRPropertyWrapperImpl(this, null, session, provider, epd); } return new JCRPropertyWrapperImpl(this, objectNode.setProperty(name, values), session, provider, epd); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String namespace, String name, String value) throws RepositoryException { String pref = objectNode.getSession().getNamespacePrefix(namespace); String key = pref + ":" + name; return setProperty(key, value); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, Value[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { final Locale locale = getSession().getLocale(); hasPropertyCache.remove(name); name = ensurePrefixedName(name); ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(name); if (epd == null) { throw new ConstraintViolationException("Couldn't find definition for property " + name); } if (!session.isSystem() && !epd.isProtected() && !epd.getDeclaringNodeType().canSetProperty(epd.getName(), values)) { throw new ConstraintViolationException("Invalid value for : " + epd.getName()); } checkLock(); values = JCRStoreService.getInstance().getInterceptorChain().beforeSetValues(this, name, epd, values); if (locale != null) { if (epd.isInternationalized()) { if (values == null) { getOrCreateI18N(locale).setProperty(name, (Value[]) null); return new JCRPropertyWrapperImpl(this, null, session, provider, epd); } return new JCRPropertyWrapperImpl(this, getOrCreateI18N(locale).setProperty(name, values, type), session, provider, epd, name); } } if (values == null) { objectNode.setProperty(name, (Value[]) null); return new JCRPropertyWrapperImpl(this, null, session, provider, epd); } return new JCRPropertyWrapperImpl(this, objectNode.setProperty(name, values, type), session, provider, epd); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, String[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { Value[] v = null; if (values != null) { v = new Value[values.length]; for (int i = 0; i < values.length; i++) { if (values[i] != null) { v[i] = getSession().getValueFactory().createValue(values[i]); } else { v[i] = null; } } } return setProperty(name, v); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, String[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { Value[] v = null; if (values != null) { v = new Value[values.length]; for (int i = 0; i < values.length; i++) { if (values[i] != null) { v[i] = getSession().getValueFactory().createValue(values[i], type); } else { v[i] = null; } } } return setProperty(name, v); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, String value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { Value v = null; if (value != null) { v = getSession().getValueFactory().createValue(value, type); } return setProperty(name, v); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, InputStream value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { Value v = null; if (value != null) { v = getSession().getValueFactory().createValue(value); } return setProperty(name, v); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, boolean value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { Value v = getSession().getValueFactory().createValue(value); return setProperty(name, v); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, double value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { Value v = getSession().getValueFactory().createValue(value); return setProperty(name, v); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, long value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { Value v = getSession().getValueFactory().createValue(value); return setProperty(name, v); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, Calendar value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { Value v = null; if (value != null) { v = getSession().getValueFactory().createValue(value); } return setProperty(name, v); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, Node value) throws ValueFormatException, VersionException, LockException, RepositoryException { Value v = null; if (value != null) { if (value instanceof JCRNodeWrapper) { value = ((JCRNodeWrapper) value).getRealNode(); } ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(name); if (epd != null) { v = getSession().getValueFactory().createValue(value, true); // in Jahia we always create weak-references } } return setProperty(name, v); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, Binary value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { Value v = null; if (value != null) { v = getSession().getValueFactory().createValue(value); } return setProperty(name, v); } /** * {@inheritDoc} */ @Override public JCRPropertyWrapper setProperty(String name, BigDecimal value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException { Value v = null; if (value != null) { v = getSession().getValueFactory().createValue(value); } return setProperty(name, v); } /** * {@inheritDoc} */ @Override public boolean hasProperty(String propertyName) throws RepositoryException { if (hasPropertyCache.containsKey(propertyName)) { return hasPropertyCache.get(propertyName); } boolean result = internalHasProperty(propertyName); hasPropertyCache.put(propertyName, result); return result; } private boolean internalHasProperty(String propertyName) throws RepositoryException { final Locale locale = getSession().getLocale(); ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(propertyName); if (epd == null) { return false; } if (locale != null && !propertyName.equals("jcr:language")) { try { if (epd.isInternationalized()) { if (hasI18N(locale, true)) { final Node localizedNode = getI18N(locale); return localizedNode.hasProperty(propertyName); } } } catch (ConstraintViolationException e) { return false; } } if (objectNode instanceof NodeImpl) { return ((NodeImpl) objectNode) .hasProperty(((SessionImpl) objectNode.getSession()).getQName(propertyName)); } return objectNode.hasProperty(propertyName); } /** * {@inheritDoc} */ @Override public boolean hasProperties() throws RepositoryException { boolean result = objectNode.hasProperties(); if (result) return true; final Locale locale = getSession().getLocale(); if (locale != null) { if (hasI18N(locale, true)) { return getI18N(locale).hasProperties(); } } return false; } /** * {@inheritDoc} */ @Override public List<JCRItemWrapper> getAncestors() throws RepositoryException { List<JCRItemWrapper> ancestors = new ArrayList<JCRItemWrapper>(); for (int i = 0; i < getDepth(); i++) { try { ancestors.add(getAncestor(i)); } catch (AccessDeniedException ade) { return ancestors; } } return ancestors; } /** * {@inheritDoc} */ @Override public boolean rename(String newName) throws RepositoryException { checkLock(); getSession().checkout(this); JCRNodeWrapper parent = getParent(); getSession().checkout(parent); // the following code is use to conserve the ordering when renaming a node, we do this only if the parent // node is orderable. String nextNodeName = null; boolean nodePositionFound = false; if (parent.getPrimaryNodeType().hasOrderableChildNodes()) { NodeIterator nodeIterator = parent.getNodes(); while (nodeIterator.hasNext()) { Node currentNode = nodeIterator.nextNode(); if (currentNode.getIdentifier().equals(getIdentifier())) { nodePositionFound = true; if (nodeIterator.hasNext()) { nextNodeName = nodeIterator.nextNode().getName(); } else { // do nothing, we will keep null as the nextNode value } break; } } } getSession().move(getPath(), parent.getPath() + "/" + newName); if (i18NobjectNodes != null) { i18NobjectNodes.clear(); } this.localPathInProvider = parent.getPath() + "/" + newName; String mountPoint = getProvider().getMountPoint(); if (mountPoint.length() > 1 && localPathInProvider.startsWith(mountPoint)) { localPathInProvider = StringUtils.substringAfter(localPathInProvider, mountPoint); } this.localPath = localPathInProvider; this.objectNode = getSession().getProviderSession(getProvider()).getNode(localPathInProvider); if ((nodePositionFound) && (parent.getPrimaryNodeType().hasOrderableChildNodes())) { parent.orderBefore(newName, nextNodeName); } return true; } /** * {@inheritDoc} */ @Override public boolean copy(String dest) throws RepositoryException { return copy(dest, getName()); } /** * {@inheritDoc} */ @Override public boolean copy(String dest, String name) throws RepositoryException { JCRNodeWrapper node = (JCRNodeWrapper) session.getItem(dest); boolean sameProvider = (provider.getKey().equals(node.getProvider().getKey())); if (!sameProvider) { copy(node, name, true); node.save(); } else { copy(node, name, true); } return true; } /** * {@inheritDoc} */ @Override public boolean copy(JCRNodeWrapper dest, String name, boolean allowsExternalSharedNodes) throws RepositoryException { Map<String, List<String>> references = new HashMap<String, List<String>>(); boolean copy = copy(dest, name, allowsExternalSharedNodes, references, null, 0, new MutableInt(0)); ReferencesHelper.resolveCrossReferences(getSession(), references, false); return copy; } /** * {@inheritDoc} */ @Override public boolean copy(JCRNodeWrapper dest, String name, boolean allowsExternalSharedNodes, Map<String, List<String>> references) throws RepositoryException { return copy(dest, name, allowsExternalSharedNodes, references, null, 0, new MutableInt(0)); } /** * {@inheritDoc} */ @Override public boolean copy(JCRNodeWrapper dest, String name, boolean allowsExternalSharedNodes, List<String> ignoreNodeTypes, int maxBatch) throws RepositoryException { Map<String, List<String>> references = new HashMap<String, List<String>>(); boolean copy = copy(dest, name, allowsExternalSharedNodes, references, ignoreNodeTypes, maxBatch, new MutableInt(0)); ReferencesHelper.resolveCrossReferences(getSession(), references, false); return copy; } /** * {@inheritDoc} */ @Override public boolean copy(JCRNodeWrapper dest, String name, boolean allowsExternalSharedNodes, Map<String, List<String>> references, List<String> ignoreNodeTypes, int maxBatch, MutableInt batchCount) throws RepositoryException { return internalCopy(dest, name, allowsExternalSharedNodes, references, ignoreNodeTypes, maxBatch, batchCount, true); } public boolean internalCopy(JCRNodeWrapper dest, String name, boolean allowsExternalSharedNodes, Map<String, List<String>> references, List<String> ignoreNodeTypes, int maxBatch, MutableInt batchCount, boolean isTopObject) throws RepositoryException { if (isTopObject) { getSession().getUuidMapping().put("top-" + getIdentifier(), StringUtils.EMPTY); } JCRNodeWrapper copy = null; try { copy = (JCRNodeWrapper) session.getItem(dest.getPath() + "/" + name); getSession().checkout(copy); } catch (PathNotFoundException ex) { // node does not exist } if (ignoreNodeTypes != null) { for (String nodeType : ignoreNodeTypes) { if (isNodeType(nodeType)) { return false; } } } batchCount.increment(); if (maxBatch > 0 && batchCount.intValue() > maxBatch) { try { session.save(); batchCount.setValue(0); } catch (ConstraintViolationException e) { // save on the next node when next node is needed (like content node for files) batchCount.setValue(maxBatch - 1); } } final Map<String, String> uuidMapping = getSession().getUuidMapping(); if (copy == null || copy.getDefinition().allowsSameNameSiblings()) { if (dest.isVersioned()) { session.checkout(dest); } String typeName = getPrimaryNodeTypeName(); try { copy = dest.addNode(name, typeName); } catch (ItemExistsException e) { copy = dest.getNode(name); } catch (ConstraintViolationException e) { logger.error("Cannot copy node", e); return false; } } try { if (copy.getProvider().isUpdateMixinAvailable()) { NodeType[] mixin = objectNode.getMixinNodeTypes(); for (NodeType aMixin : mixin) { if (!Constants.forbiddenMixinToCopy.contains(aMixin.getName())) { copy.addMixin(aMixin.getName()); } } } } catch (RepositoryException e) { logger.error("Error adding mixin types to copy", e); } if (copy != null) { uuidMapping.put(getIdentifier(), copy.getIdentifier()); if (hasProperty("jcr:language")) { copy.setProperty("jcr:language", getProperty("jcr:language").getString()); } copyProperties(copy, references); } NodeIterator ni = getNodes(); while (ni.hasNext()) { JCRNodeWrapper source = (JCRNodeWrapper) ni.next(); if (source.isNodeType("mix:shareable")) { if (uuidMapping.containsKey(source.getIdentifier())) { // ugly save because to make node really shareable session.save(); copy.clone(session.getNodeByUUID(uuidMapping.get(source.getIdentifier())), source.getName()); } else if (allowsExternalSharedNodes) { copy.clone(source, source.getName()); } else { doCopy(source, copy, source.getName(), allowsExternalSharedNodes, references, ignoreNodeTypes, maxBatch, batchCount, false); } } else if (!source.isNodeType(Constants.JAHIAMIX_MARKED_FOR_DELETION_ROOT) && !source.isNodeType(Constants.JAHIANT_REFERENCEINFIELD)) { doCopy(source, copy, source.getName(), allowsExternalSharedNodes, references, ignoreNodeTypes, maxBatch, batchCount, false); } } return true; } @Override public void copyProperties(JCRNodeWrapper destinationNode, Map<String, List<String>> references) throws RepositoryException { PropertyIterator props = getProperties(); while (props.hasNext()) { Property property = props.nextProperty(); boolean b = !property.getDefinition().getDeclaringNodeType().isMixin() || destinationNode.getProvider().isUpdateMixinAvailable(); try { if (!Constants.forbiddenPropertiesToCopy.contains(property.getName()) && b) { if (property.getType() == PropertyType.REFERENCE || property.getType() == PropertyType.WEAKREFERENCE) { if (property.getDefinition().isMultiple() && (property.isMultiple())) { Value[] values = property.getValues(); for (Value value : values) { keepReference(destinationNode, references, property, value.getString()); } } else { keepReference(destinationNode, references, property, property.getValue().getString()); } } if (property.getDefinition().isMultiple() && (property.isMultiple())) { destinationNode.setProperty(property.getName(), property.getValues()); } else { destinationNode.setProperty(property.getName(), property.getValue()); } } } catch (Exception e) { logger.debug("Unable to copy property '" + property.getName() + "'. Skipping.", e); } } } private void keepReference(JCRNodeWrapper destinationNode, Map<String, List<String>> references, Property property, String value) throws RepositoryException { if (!references.containsKey(value)) { references.put(value, new ArrayList<String>()); } references.get(value).add(destinationNode.getIdentifier() + "/" + property.getName()); } /** * {@inheritDoc} */ @Override public void remove() throws VersionException, LockException, ConstraintViolationException, RepositoryException { getSession().unregisterNewNode(this); if (!this.hasNodes()) { getSession().removeFromCache(this); } else { getSession().flushCaches(); } item.remove(); } /** * {@inheritDoc} */ @Override public Lock lock(boolean isDeep, boolean isSessionScoped) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException { return objectNode.lock(isDeep, isSessionScoped); } /** * {@inheritDoc} * * @param type */ @Override public boolean lockAndStoreToken(String type) throws RepositoryException { String l = getSession().isSystem() ? " system " : getSession().getUserID(); return lockAndStoreToken(type, l); } /** * {@inheritDoc} * * @param type */ @Override public boolean lockAndStoreToken(String type, String userID) throws RepositoryException { if (!isNodeType("jmix:lockable")) { return false; } String token = null; String i18nToken = null; Node locked = null; Node i18nLocked = null; if (!objectNode.isLocked()) { lockNode(objectNode); locked = objectNode; } else { Property property = objectNode.getProperty("j:locktoken"); token = property.getString(); objectNode.getSession().getWorkspace().getLockManager().addLockToken(token); } try { addLockTypeValue(objectNode, userID + ":" + type); if (session.getLocale() != null && !isNodeType(Constants.JAHIANT_TRANSLATION)) { Node trans = null; try { trans = getI18N(session.getLocale()); if (!trans.isLocked()) { lockNode(trans); i18nLocked = trans; } else { Property property = trans.getProperty("j:locktoken"); i18nToken = property.getString(); trans.getSession().getWorkspace().getLockManager().addLockToken(i18nToken); } addLockTypeValue(trans, userID + ":" + type); } catch (ItemNotFoundException e) { } } objectNode.getSession().save(); } catch (RepositoryException e) { // Clean locks before leaving try { if (locked != null) { locked.getSession().getWorkspace().getLockManager().unlock(locked.getPath()); } } catch (RepositoryException unlockEx) { logger.warn("Error when unlocking unsuccessful lock on node: " + getPath(), unlockEx); } try { if (i18nLocked != null) { i18nLocked.getSession().getWorkspace().getLockManager().unlock(i18nLocked.getPath()); } } catch (RepositoryException unlockEx) { logger.warn("Error when unlocking unsuccessful lock on node: " + getPath(), unlockEx); } if (token != null) { objectNode.getSession().getWorkspace().getLockManager().removeLockToken(token); } if (i18nToken != null) { objectNode.getSession().getWorkspace().getLockManager().removeLockToken(i18nToken); } throw e; } return true; } private void lockNode(final Node objectNode) throws RepositoryException { getSession().checkout(objectNode); Lock lock = objectNode.lock(false, false); if (lock.getLockToken() != null) { try { objectNode.setProperty("j:locktoken", lock.getLockToken()); // objectNode.getSession().removeLockToken(lock.getLockToken()); } catch (RepositoryException e) { logger.error("Cannot store token for " + getPath(), e); objectNode.unlock(); } } else { logger.error("Lost lock ! " + localPathInProvider); } } private void addLockTypeValue(final Node objectNode, String l) throws RepositoryException { getSession().checkout(objectNode); if (objectNode.hasProperty("j:lockTypes")) { Property property = objectNode.getProperty("j:lockTypes"); Value[] oldValues = property.getValues(); boolean addValue = true; for (Value oldValue : oldValues) { if (l.equals(oldValue.getString())) { addValue = false; break; } } //Avoid having twice the same lock if (addValue) { Value[] newValues = new Value[oldValues.length + 1]; System.arraycopy(oldValues, 0, newValues, 0, oldValues.length); newValues[oldValues.length] = getSession().getValueFactory().createValue(l); property.setValue(newValues); } } else { objectNode.setProperty("j:lockTypes", new String[] { l }); } } /** * {@inheritDoc} */ @Override public boolean isLocked() { try { return objectNode.isLocked(); } catch (RepositoryException e) { return false; } } /** * {@inheritDoc} */ @Override public boolean isLockable() { try { return objectNode.isNodeType(Constants.MIX_LOCKABLE); } catch (RepositoryException e) { return false; } } @Override public List<Locale> getLockedLocales() throws RepositoryException { List<Locale> r = new ArrayList<Locale>(); NodeIterator ni = objectNode.getNodes(TRANSLATION_NODES_PATTERN); while (ni.hasNext()) { Node n = ni.nextNode(); if (n.isLocked()) { r.add(LanguageCodeConverters.languageCodeToLocale(n.getProperty("jcr:language").getString())); } } return r; } public List<Locale> getLockedLocalesForUserAndType(String type) throws RepositoryException { List<Locale> r = new ArrayList<Locale>(); NodeIterator ni = getI18Ns(); while (ni.hasNext()) { Node n = ni.nextNode(); if (n.isLocked() && n.hasProperty("j:lockTypes")) { String l = (getSession().isSystem() ? " system " : getSession().getUserID()) + ":" + type; Value[] v = n.getProperty("j:lockTypes").getValues(); for (Value value : v) { if (value.getString().equals(l)) { r.add(LanguageCodeConverters.getLocaleFromCode(n.getProperty("jcr:language").getString())); break; } } } } return r; } /** * {@inheritDoc} */ @Override public javax.jcr.lock.Lock getLock() { try { final javax.jcr.lock.Lock lock = objectNode.getLock(); return new javax.jcr.lock.Lock() { @Override public String getLockOwner() { return lock.getLockOwner(); } @Override public boolean isDeep() { return lock.isDeep(); } @Override public long getSecondsRemaining() throws RepositoryException { return lock.getSecondsRemaining(); } @Override public boolean isLockOwningSession() { return lock.isLockOwningSession(); } @Override public Node getNode() { try { return getProvider().getNodeWrapper(lock.getNode(), getSession()); } catch (RepositoryException e) { logger.warn("Can't get wrapper for node holding lock", e); return JCRNodeWrapperImpl.this; } } @Override public String getLockToken() { return lock.getLockToken(); } @Override public boolean isLive() throws RepositoryException { return lock.isLive(); } @Override public boolean isSessionScoped() { return lock.isSessionScoped(); } @Override public void refresh() throws LockException, RepositoryException { lock.isSessionScoped(); } }; } catch (RepositoryException e) { return null; } } /** * {@inheritDoc} */ @Override public void unlock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException { objectNode.unlock(); } @Override public void unlock(String type) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException { if (getSession().getUser() != null) { unlock(type, getSession().getUser().getName()); } else { unlock(type, getSession().getUserID()); } } @Override public void unlock(String type, String userID) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException { if (!isLocked()) { throw new LockException("Node not locked"); } if (session.getLocale() != null && !isNodeType(Constants.JAHIANT_TRANSLATION) && hasI18N(session.getLocale(), false)) { Node trans = getI18N(session.getLocale(), false); if (trans.isLocked()) { unlock(trans, type, userID); } } if (isNodeType(Constants.JAHIANT_TRANSLATION) && !getLockedLocalesForUserAndType(type).isEmpty()) { return; } unlock(objectNode, type, userID); } private void unlock(final Node objectNode, String type, String userID) throws RepositoryException { if (objectNode.hasProperty("j:locktoken")) { Property property = objectNode.getProperty("j:locktoken"); String token = property.getString(); Value[] types = objectNode.getProperty("j:lockTypes").getValues(); for (Value value : types) { String owner = StringUtils.substringBefore(value.getString(), ":"); String currentType = StringUtils.substringAfter(value.getString(), ":"); if (currentType.equals(type)) { if (userID.equals(owner)) { objectNode.getSession().addLockToken(token); getSession().checkout(objectNode); List<Value> valueList = new ArrayList<Value>(Arrays.asList(types)); valueList.remove(value); if (valueList.isEmpty()) { getSession().save(); objectNode.unlock(); property.remove(); objectNode.getProperty("j:lockTypes").remove(); } else { objectNode.setProperty("j:lockTypes", valueList.toArray(new Value[valueList.size()])); } getSession().save(); return; } } } } else { objectNode.unlock(); } } @Override public void clearAllLocks() throws RepositoryException { if (!isNodeType(Constants.JAHIANT_TRANSLATION)) { NodeIterator ni = objectNode.getNodes(TRANSLATION_NODES_PATTERN); while (ni.hasNext()) { Node trans = (Node) ni.next(); clearAllLocks(trans); } } if (isNodeType(Constants.JAHIANT_TRANSLATION) && !getLockedLocales().isEmpty()) { return; } clearAllLocks(objectNode); } private void clearAllLocks(final Node objectNode) throws RepositoryException { getSession().checkout(objectNode); if (objectNode.isLocked()) { objectNode.unlock(); } if (objectNode.hasProperty("j:locktoken")) { objectNode.getProperty("j:locktoken").remove(); getSession().save(); } if (objectNode.hasProperty("j:lockTypes")) { objectNode.getProperty("j:lockTypes").remove(); getSession().save(); } } @Override public void checkLock() throws RepositoryException { if (!session.isSystem() && isLocked()) { List<String> owners = getLockOwners(objectNode); if (owners.size() == 1 && owners.contains(session.getUserID())) { objectNode.getSession().addLockToken(objectNode.getProperty("j:locktoken").getString()); } else { boolean lockOwningSession = false; if (owners.size() == 0) { Lock lock = objectNode.getLock(); lockOwningSession = lock != null && lock.isLockOwningSession(); } if (!lockOwningSession) { if (logger.isDebugEnabled()) { logger.debug( "Node " + objectNode.getPath() + " locked. Locks info:" + getLockInfos(objectNode)); } throw new LockException("Node locked."); } } if (session.getLocale() != null) { try { Node i18n = getI18N(session.getLocale()); if (i18n.isLocked()) { owners = getLockOwners(i18n); if (owners.size() == 1 && owners.contains(session.getUserID())) { i18n.getSession().addLockToken(i18n.getProperty("j:locktoken").getString()); } else { boolean lockOwningSession = false; if (owners.size() == 0) { Lock lock = i18n.getLock(); lockOwningSession = lock != null && lock.isLockOwningSession(); } if (!lockOwningSession) { if (logger.isDebugEnabled()) { logger.debug("Node i18n " + i18n.getPath() + " locked. Locks info:" + getLockInfos(i18n)); } throw new LockException("Node locked."); } } } } catch (ItemNotFoundException e) { if (logger.isDebugEnabled()) { logger.debug("checkLock : no i18n node for node " + localPathInProvider); } } } } } /** * {@inheritDoc} */ @Override public boolean holdsLock() throws RepositoryException { return objectNode.holdsLock(); } /** * {@inheritDoc} */ @Override public String getLockOwner() throws RepositoryException { if (getLock() == null) { return null; } if (!"shared".equals(provider.getAuthenticationType())) { List<String> lockOwners = getLockOwners(objectNode); if (lockOwners.isEmpty()) { return null; } if (lockOwners.isEmpty()) { return StringUtils.EMPTY; } if (lockOwners.size() == 1) { return String.valueOf(lockOwners.get(0)).trim(); } else { StringBuilder owners = new StringBuilder(); for (String s : lockOwners) { owners.append(s).append(" "); } return owners.toString().trim(); } } else { return getSession().getUserID(); } } @Override public Map<String, List<String>> getLockInfos() throws RepositoryException { Map<String, List<String>> locks = new HashMap<String, List<String>>(); List<String> lockInfos = getLockInfos(objectNode); if (!lockInfos.isEmpty()) { locks.put(null, lockInfos); } NodeIterator ni = getI18Ns(); while (ni.hasNext()) { Node n = ni.nextNode(); lockInfos = getLockInfos(n); if (!lockInfos.isEmpty()) { locks.put(n.getProperty("jcr:language").getString(), lockInfos); } } return locks; } private List<String> getLockOwners(Node node) throws RepositoryException { List<String> types = getLockInfos(node); List<String> r = new ArrayList<String>(); for (String type : types) { String owner = StringUtils.substringBefore(type, ":"); if (!r.contains(owner)) { r.add(owner); } } return r; } private List<String> getLockInfos(Node node) throws RepositoryException { List<String> r = new ArrayList<String>(); Value[] values = null; try { values = node.getProperty("j:lockTypes").getValues(); } catch (PathNotFoundException e) { // no j:lockTypes property found -> skipping } if (values != null) { for (Value value : values) { if (!r.contains(value.getString())) { r.add(value.getString()); } } } return r; } /** * {@inheritDoc} */ @Override public void versionFile() { try { objectNode.addMixin(Constants.MIX_VERSIONABLE); } catch (RepositoryException e) { logger.error("Error while adding versionable mixin type", e); } } /** * {@inheritDoc} */ @Override public boolean isVersioned() { try { return getProvider().isVersioningAvailable() && objectNode.isNodeType(Constants.MIX_VERSIONABLE); } catch (RepositoryException e) { logger.error("Error while checking if object node is versioned", e); } return false; } /** * {@inheritDoc} */ @Override public void checkpoint() { try { JCRObservationManager.doWorkspaceWriteCall(getSession(), JCRObservationManager.NODE_CHECKPOINT, new JCRCallback<Object>() { @Override public Object doInJCR(JCRSessionWrapper session) throws RepositoryException { session.getWorkspace().getVersionManager().checkpoint(localPathInProvider); return null; } }); } catch (RepositoryException e) { logger.error("Error setting checkpoint for node " + getPath(), e); } } /** * {@inheritDoc} */ @Override public List<String> getVersions() { List<String> results = new ArrayList<String>(); try { VersionHistory vh = objectNode.getVersionHistory(); VersionIterator vi = vh.getAllVersions(); // forget root version vi.nextVersion(); while (vi.hasNext()) { Version version = vi.nextVersion(); results.add(version.getName()); } } catch (RepositoryException e) { logger.error("Error while retrieving versions", e); } return results; } /** * {@inheritDoc} */ @Override public List<Version> getVersionsAsVersion() { List<Version> results = new ArrayList<Version>(); try { VersionHistory vh = objectNode.getVersionHistory(); VersionIterator vi = vh.getAllVersions(); // forget root version vi.nextVersion(); while (vi.hasNext()) { Version version = vi.nextVersion(); results.add(version); } Collections.sort(results, new Comparator<Version>() { @Override public int compare(Version o1, Version o2) { try { return o1.getCreated().compareTo(o2.getCreated()); } catch (RepositoryException e) { return -1; } } }); } catch (RepositoryException e) { logger.error("Error while retrieving versions", e); } return results; } /** * {@inheritDoc} */ @Override public List<VersionInfo> getVersionInfos() throws RepositoryException { return ServicesRegistry.getInstance().getJCRVersionService().getVersionInfos(session, this); } /** * {@inheritDoc} */ @Override public List<VersionInfo> getLinearVersionInfos() throws RepositoryException { return ServicesRegistry.getInstance().getJCRVersionService().getLinearVersionInfos(session, this); } /** * {@inheritDoc} */ @Override public JCRStoreProvider getJCRProvider() { return provider; } /** * {@inheritDoc} */ @Override public JCRStoreProvider getProvider() { return provider; } /** * {@inheritDoc} */ @Override public void orderBefore(String s, String s1) throws UnsupportedRepositoryOperationException, VersionException, ConstraintViolationException, ItemNotFoundException, LockException, RepositoryException { checkLock(); objectNode.orderBefore(s, s1); } /** * {@inheritDoc} */ @Override public JCRItemWrapper getPrimaryItem() throws ItemNotFoundException, RepositoryException { return provider.getItemWrapper(objectNode.getPrimaryItem(), session); } /** * {@inheritDoc} */ @Override public int getIndex() throws RepositoryException { return objectNode.getIndex(); } /** * {@inheritDoc} */ @Override public PropertyIterator getReferences() throws RepositoryException { return new PropertyIteratorImpl(objectNode.getReferences(), getSession(), getProvider()); } /** * {@inheritDoc} */ @Override public boolean hasNode(String s) throws RepositoryException { if (!objectNode.hasNode(s)) { // no local node found -> check mounted providers if (provider.getService() != null && provider.getSessionFactory().areMultipleMountPointsRegistered()) { String targetPath = getPath() + '/' + s; JCRStoreProvider targetProvider = provider.getSessionFactory().getProvider(targetPath, false); if (targetProvider != null && targetProvider != provider) { // we found a provider which can handle the specified path if (targetProvider.getMountPoint().equals(targetPath)) { // it is the provider mount point itself return true; } else { // it is a child node -> check its existence JCRNodeWrapper providerRoot = targetProvider .getNodeWrapper(session.getProviderSession(targetProvider) .getNode(targetProvider.getRelativeRoot().isEmpty() ? "/" : targetProvider.getRelativeRoot()), "/", this, session); String childPath = StringUtils.substringAfter(targetPath, targetProvider.getMountPoint() + "/"); if (childPath.length() > 0) { return providerRoot.hasNode(childPath); } } } } return false; } if (Constants.LIVE_WORKSPACE.equals(getSession().getWorkspace().getName()) && !s.startsWith("j:translation")) { // in live workspace we also check the validity of the node final JCRNodeWrapper wrapper; try { wrapper = (JCRNodeWrapper) getNode(s); } catch (RepositoryException e) { return false; } return wrapper.checkValidity(); } return true; } /** * {@inheritDoc} */ @Override public boolean hasNodes() throws RepositoryException { if (provider.getService() != null) { Map<String, JCRStoreProvider> allMountPoints = provider.getSessionFactory().getMountPoints(); if (allMountPoints.size() > 1) { String pathPrefix = getPath() + "/"; for (String entry : allMountPoints.keySet()) { if (!entry.equals("/") && entry.startsWith(pathPrefix) && entry.indexOf('/', pathPrefix.length() - 1) == -1) { return true; } } } } if (!objectNode.hasNodes()) { // underlying node has no children return false; } // if we are in live or session is localized, we need additional checks boolean inLive = Constants.LIVE_WORKSPACE.equals(getSession().getWorkspace().getName()); if (inLive || session.getLocale() != null) { NodeIterator ni = objectNode.getNodes(); while (ni.hasNext()) { try { Node child = ni.nextNode(); String childName = child.getName(); if (session.getLocale() != null && childName.startsWith(TRANSLATION_PREFIX)) { // skip j:translation_* nodes in localized session continue; } if (getNode(childName).checkValidity()) { return true; } } catch (PathNotFoundException e) { // ignore } } return false; } return true; } /** * {@inheritDoc} */ @Override public JCRVersion checkin() throws VersionException, UnsupportedRepositoryOperationException, InvalidItemStateException, LockException, RepositoryException { return (JCRVersion) session.getWorkspace().getVersionManager().checkin(getPath()); } /** * {@inheritDoc} */ @Override public void checkout() throws UnsupportedRepositoryOperationException, LockException, RepositoryException { session.checkout(this); } /** * {@inheritDoc} */ @Override public void doneMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException { VersionManager versionManager = session.getWorkspace().getVersionManager(); versionManager.doneMerge(localPathInProvider, ((JCRVersion) version).getRealNode()); } /** * {@inheritDoc} */ @Override public void cancelMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException { VersionManager versionManager = session.getWorkspace().getVersionManager(); versionManager.cancelMerge(localPathInProvider, ((JCRVersion) version).getRealNode()); } /** * {@inheritDoc} */ @Override public void update(final String srcWorkspace) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException { JCRObservationManager.doWorkspaceWriteCall(getSession(), JCRObservationManager.NODE_UPDATE, new JCRCallback<Object>() { @Override public Object doInJCR(JCRSessionWrapper session) throws RepositoryException { objectNode.update(srcWorkspace); return null; } }); } /** * {@inheritDoc} */ @Override public JCRNodeIteratorWrapper merge(String s, boolean b) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public String getCorrespondingNodePath(String s) throws ItemNotFoundException, NoSuchWorkspaceException, AccessDeniedException, RepositoryException { String nodePath = objectNode.getCorrespondingNodePath(s); if (provider.getMountPoint().equals("/")) { return nodePath; } else { return nodePath.equals("/") ? provider.getMountPoint() : provider.getMountPoint() + nodePath; } } /** * {@inheritDoc} */ @Override public boolean isCheckedOut() throws RepositoryException { VersionManager versionManager = session.getProviderSession(provider).getWorkspace().getVersionManager(); boolean co = versionManager.isCheckedOut(localPathInProvider); if (co && session.getLocale() != null) { try { co &= versionManager.isCheckedOut(getI18N(session.getLocale()).getPath()); } catch (ItemNotFoundException e) { if (logger.isDebugEnabled()) { logger.debug("isCheckedOut : no i18n node for node " + localPathInProvider); } } } return co; } /** * {@inheritDoc} */ @Override public void restore(String s, boolean b) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException { getRealNode().restore(s, b); } /** * {@inheritDoc} */ @Override public void restore(Version version, boolean b) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, RepositoryException { getRealNode().restore(version instanceof JCRVersion ? ((JCRVersion) version).getRealNode() : version, b); } /** * {@inheritDoc} */ @Override public void restore(Version version, String s, boolean b) throws PathNotFoundException, ItemExistsException, VersionException, ConstraintViolationException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException { getRealNode().restore(version instanceof JCRVersion ? ((JCRVersion) version).getRealNode() : version, s, b); } /** * {@inheritDoc} */ @Override public void restoreByLabel(String s, boolean b) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException { getRealNode().restoreByLabel(s, b); } /** * {@inheritDoc} */ @Override public VersionHistory getVersionHistory() throws UnsupportedRepositoryOperationException, RepositoryException { return (VersionHistory) getProvider().getNodeWrapper((Node) getRealNode().getVersionHistory(), (JCRSessionWrapper) getSession()); } /** * {@inheritDoc} */ @Override public Version getBaseVersion() throws UnsupportedRepositoryOperationException, RepositoryException { return (Version) getProvider().getNodeWrapper((Node) getRealNode().getBaseVersion(), (JCRSessionWrapper) getSession()); } /** * {@inheritDoc} */ @Override public JCRFileContent getFileContent() { if (fileContent == null) { fileContent = new JCRFileContent(this, objectNode); } return fileContent; } /** * {@inheritDoc} */ @Override public ExtendedPropertyDefinition getApplicablePropertyDefinition(String propertyName) throws RepositoryException { return getApplicablePropertyDefinition(propertyName, 0, false); } /** * {@inheritDoc} */ @Override public ExtendedPropertyDefinition getApplicablePropertyDefinition(String propertyName, int requiredPropertyType, boolean isMultiple) throws RepositoryException { ExtendedPropertyDefinition result = null; if (applicablePropertyDefinition.containsKey(propertyName)) { result = applicablePropertyDefinition.get(propertyName); return result; } List<ExtendedNodeType> types = new ArrayList<ExtendedNodeType>(); Iterator<ExtendedNodeType> iterator = getNodeTypesIterator(); while (iterator.hasNext()) { ExtendedNodeType type = iterator.next(); final Map<String, ExtendedPropertyDefinition> definitionMap = type.getPropertyDefinitionsAsMap(); applicablePropertyDefinition.putAll(definitionMap); if (definitionMap.containsKey(propertyName)) { result = definitionMap.get(propertyName); return result; } types.add(type); } if (isNodeType(Constants.JAHIANT_TRANSLATION) && !propertyName.equals("jcr:language")) { result = getParent().getApplicablePropertyDefinition(propertyName); if (result != null) { applicablePropertyDefinition.put(propertyName, result); return result; } } for (ExtendedNodeType type : types) { for (ExtendedPropertyDefinition epd : type.getUnstructuredPropertyDefinitions().values()) { // check type .. ? if ((requiredPropertyType == 0 || epd.getRequiredType() == 0 || epd.getRequiredType() == requiredPropertyType) && isMultiple == epd.isMultiple()) { result = epd; applicablePropertyDefinition.put(propertyName, result); return result; } } } applicablePropertyDefinition.put(propertyName, result); return result; } @Override public List<ExtendedPropertyDefinition> getReferenceProperties() throws RepositoryException { List<ExtendedPropertyDefinition> defs = new ArrayList<ExtendedPropertyDefinition>(); List<ExtendedNodeType> types = new ArrayList<ExtendedNodeType>(); Iterator<ExtendedNodeType> iterator = getNodeTypesIterator(); if (isNodeType(Constants.JAHIANT_TRANSLATION)) { return getParent().getReferenceProperties(); } while (iterator.hasNext()) { ExtendedNodeType type = iterator.next(); final Map<String, ExtendedPropertyDefinition> definitionMap = type.getPropertyDefinitionsAsMap(); for (ExtendedPropertyDefinition definition : definitionMap.values()) { if (definition.getRequiredType() == PropertyType.REFERENCE || definition.getRequiredType() == PropertyType.WEAKREFERENCE) { defs.add(definition); } } types.add(type); } for (ExtendedNodeType type : types) { for (ExtendedPropertyDefinition definition : type.getUnstructuredPropertyDefinitions().values()) { if (definition.getRequiredType() == PropertyType.REFERENCE || definition.getRequiredType() == PropertyType.WEAKREFERENCE) { defs.add(definition); } } } return defs; } @Override public ExtendedNodeDefinition getApplicableChildNodeDefinition(String childName, String nodeType) throws ConstraintViolationException, RepositoryException { ExtendedNodeType requiredType = NodeTypeRegistry.getInstance().getNodeType(nodeType); List<ExtendedNodeType> types = new ArrayList<ExtendedNodeType>(); Iterator<ExtendedNodeType> iterator = getNodeTypesIterator(); while (iterator.hasNext()) { ExtendedNodeType type = iterator.next(); final Map<String, ExtendedNodeDefinition> definitionMap = type.getChildNodeDefinitionsAsMap(); if (definitionMap.containsKey(childName)) { ExtendedNodeDefinition epd = definitionMap.get(childName); for (String req : epd.getRequiredPrimaryTypeNames()) { if (requiredType.isNodeType(req)) { return epd; } } throw new ConstraintViolationException("Definition type for " + childName + " on node " + getName() + " (" + getPrimaryNodeTypeName() + ") does not match " + nodeType); } types.add(type); } for (ExtendedNodeType type : types) { for (ExtendedNodeDefinition epd : type.getUnstructuredChildNodeDefinitions().values()) { for (String req : epd.getRequiredPrimaryTypeNames()) { if (requiredType.isNodeType(req)) { return epd; } } } } throw new ConstraintViolationException("Cannot find definition for " + childName + " on node " + getName() + " (" + getPrimaryNodeTypeName() + ")"); } private Iterator<ExtendedNodeType> getNodeTypesIterator() { return new Iterator<ExtendedNodeType>() { int i = 0; ExtendedNodeType next; boolean fetched = false; Iterator<ExtendedNodeType> mix = null; @Override public boolean hasNext() { if (!fetched) { try { if (i == 0) { next = getPrimaryNodeType(); } else if (i == 1 && isNodeType("nt:frozenNode")) { next = NodeTypeRegistry.getInstance() .getNodeType(objectNode.getProperty("jcr:frozenPrimaryType").getString()); } else { if (mix == null) { mix = Arrays.asList(getMixinNodeTypes()).iterator(); } if (mix.hasNext()) { next = mix.next(); } else { next = null; } } } catch (RepositoryException e) { logger.warn(e.getMessage(), e); } finally { i++; } fetched = true; } return (next != null); } private boolean isNodeType(String nodeType) { boolean isNodeType = false; try { isNodeType = objectNode.isNodeType(nodeType); } catch (RepositoryException e) { logger.warn(e.getMessage(), e); } return isNodeType; } @Override public ExtendedNodeType next() { if (!fetched) { hasNext(); } if (next != null) { fetched = false; return next; } throw new NoSuchElementException(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } /** * {@inheritDoc} */ @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || this.getClass() != o.getClass()) return false; final JCRNodeWrapper fileNodeWrapper = (JCRNodeWrapper) o; return !(getPath() != null ? !getPath().equals(fileNodeWrapper.getPath()) : fileNodeWrapper.getPath() != null); } /** * {@inheritDoc} */ @Override public int hashCode() { return (getPath() != null ? getPath().hashCode() : 0); } /** * {@inheritDoc} */ @Override public PropertyIterator getProperties(String[] strings) throws RepositoryException { final Locale locale = getSession().getLocale(); if (locale != null) { return new LazyPropertyIterator(this, locale, strings); } return new LazyPropertyIterator(this, null, strings); } /** * {@inheritDoc} */ @Override public String getIdentifier() throws RepositoryException { return objectNode.getIdentifier(); } /** * {@inheritDoc} */ @Override public PropertyIterator getReferences(String name) throws RepositoryException { return new PropertyIteratorImpl(objectNode.getReferences(name), getSession(), getProvider()); } /** * {@inheritDoc} */ @Override public PropertyIterator getWeakReferences() throws RepositoryException { return getWeakReferences(null); } /** * {@inheritDoc} * * @throws UnsupportedRepositoryOperationException as long as Jahia doesn't support it */ @Override public PropertyIterator getWeakReferences(String name) throws RepositoryException { // shortcut if node isn't referenceable if (!isNodeType(Constants.MIX_REFERENCEABLE)) { return new PropertyIteratorImpl(PropertyIteratorAdapter.EMPTY, getSession(), getProvider()); } return getSession().getWeakReferences(this, name); } /** * {@inheritDoc} * * @throws UnsupportedRepositoryOperationException as long as Jahia doesn't support it */ @Override public void setPrimaryType(String nodeTypeName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException { throw new UnsupportedRepositoryOperationException(); } /** * {@inheritDoc} */ @Override public JCRNodeIteratorWrapper getSharedSet() throws RepositoryException { List<JCRNodeWrapper> list = new ArrayList<JCRNodeWrapper>(); NodeIterator ni = objectNode.getSharedSet(); while (ni.hasNext()) { Node node = ni.nextNode(); JCRNodeWrapper child = provider.getNodeWrapper(node, session); list.add(child); } return new NodeIteratorImpl(list.iterator(), list.size()); } /** * {@inheritDoc} */ @Override public void removeSharedSet() throws VersionException, LockException, ConstraintViolationException, RepositoryException { objectNode.removeSharedSet(); } /** * {@inheritDoc} */ @Override public void removeShare() throws VersionException, LockException, ConstraintViolationException, RepositoryException { objectNode.removeShare(); } /** * {@inheritDoc} * * @throws UnsupportedRepositoryOperationException as long as Jahia doesn't support it */ @Override public void followLifecycleTransition(String transition) throws UnsupportedRepositoryOperationException, InvalidLifecycleTransitionException, RepositoryException { throw new UnsupportedRepositoryOperationException(); } /** * {@inheritDoc} */ @Override public String[] getAllowedLifecycleTransistions() throws UnsupportedRepositoryOperationException, RepositoryException { return new String[0]; } @Override public JCRNodeWrapper clone(JCRNodeWrapper sharedNode, String name) throws ItemExistsException, VersionException, ConstraintViolationException, LockException, RepositoryException { if (!sharedNode.isNodeType("jmix:shareable")) { getSession().checkout(sharedNode); sharedNode.addMixin("jmix:shareable"); sharedNode.getRealNode().getSession().save(); try { final String path = sharedNode.getCorrespondingNodePath(Constants.LIVE_WORKSPACE); JCRTemplate.getInstance().doExecuteWithSystemSessionAsUser(null, Constants.LIVE_WORKSPACE, null, new JCRCallback<Object>() { public Object doInJCR(JCRSessionWrapper session) throws RepositoryException { JCRNodeWrapper n = session.getNode(path); getSession().checkout(n); n.addMixin("jmix:shareable"); n.getRealNode().getSession().save(); return null; } }); } catch (ItemNotFoundException e) { } catch (RepositoryException e) { logger.warn(e.getMessage(), e); } } if (getRealNode() instanceof NodeImpl && sharedNode.getRealNode() instanceof NodeImpl) { String uri = ""; if (name.contains(":")) { uri = session.getNamespaceURI(StringUtils.substringBefore(name, ":")); name = StringUtils.substringAfter(name, ":"); } org.apache.jackrabbit.spi.Name jrname = NameFactoryImpl.getInstance().create(uri, name); NodeImpl node = (NodeImpl) getRealNode(); try { return provider.getNodeWrapper(node.clone((NodeImpl) sharedNode.getRealNode(), jrname), buildSubnodePath(name), this, session); } catch (RepositoryException e) { logger.error(e.getMessage(), e); } } throw new UnsupportedRepositoryOperationException(); } @Override public boolean checkValidity() { try { if (getPath().startsWith("/sites")) { final JCRSessionWrapper jcrSessionWrapper = getSession(); if (Constants.LIVE_WORKSPACE.equals(jcrSessionWrapper.getWorkspace().getName()) && !JCRStoreService .getInstance().getNoValidityCheckTypes().contains(getPrimaryNodeTypeName())) { boolean isLocaleDefined = jcrSessionWrapper.getLocale() != null; if (isLocaleDefined) { if (objectNode.hasProperty("j:published") && !objectNode.getProperty("j:published").getBoolean()) { // Node is completely unpublished return false; } else { // if (language is invalid OR (the node does'nt have an i18n subnode published AND the node have an i18n subnode)) if (JCRContentUtils.isLanguageInvalid(objectNode, jcrSessionWrapper.getLocale().toString()) || (!hasI18N(jcrSessionWrapper.getLocale(), true) && hasI18N(jcrSessionWrapper.getLocale(), true, false))) { return false; } } } boolean result = checkLanguageValidity(null); if (result && isLocaleDefined) { result = VisibilityService.getInstance().matchesConditions(this); } return result; } } if (getProvider().isDefault()) { return !objectNode.hasProperty("j:isExternalProviderRoot"); } } catch (RepositoryException e) { return false; } return true; } @Override public boolean checkLanguageValidity(Set<String> languages) { final JCRSessionWrapper jcrSessionWrapper = getSession(); try { Locale locale = jcrSessionWrapper.getLocale(); if (locale != null) { JCRSiteNode siteNode = getResolveSite(); if (siteNode != null) { Set<String> mandatoryLanguages = siteNode.getMandatoryLanguages(); List<Locale> locales = jcrSessionWrapper.isLive() ? siteNode.getActiveLiveLanguagesAsLocales() : siteNode.getLanguagesAsLocales(); if (locales.size() == 0) { return true; } // BACKLOG-3125 - perhaps getNoLanguageValidityCheckTypes().contains needs to be applied more generally in this method if (!JCRStoreService.getInstance().getNoLanguageValidityCheckTypes() .contains(getPrimaryNodeTypeName()) && !locales.contains(locale) && !site.isAllowsUnlistedLanguages() && hasI18N(locale)) { return false; } for (String mandatoryLanguage : mandatoryLanguages) { if (!checkI18nAndMandatoryPropertiesForLocale( LanguageCodeConverters.getLocaleFromCode(mandatoryLanguage))) { return false; } } } boolean b = checkI18nAndMandatoryPropertiesForLocale(locale); if (!b && siteNode != null && siteNode.isMixLanguagesActive()) { b = checkI18nAndMandatoryPropertiesForLocale( LanguageCodeConverters.getLocaleFromCode(siteNode.getDefaultLanguage())); } return b; } else if (languages != null) { for (String language : languages) { if (checkI18nAndMandatoryPropertiesForLocale( LanguageCodeConverters.getLocaleFromCode(language))) { JCRSiteNode siteNode = getResolveSite(); if (siteNode != null) { Set<String> mandatoryLanguages = siteNode.getMandatoryLanguages(); if (mandatoryLanguages == null || mandatoryLanguages.isEmpty()) { return true; } for (String mandatoryLanguage : mandatoryLanguages) { if (!checkI18nAndMandatoryPropertiesForLocale( LanguageCodeConverters.getLocaleFromCode(mandatoryLanguage))) { return false; } } } } else { return false; } } } } catch (RepositoryException e) { return false; } return true; } @Override public boolean hasTranslations() throws RepositoryException { return objectNode.getNodes(TRANSLATION_NODES_PATTERN).hasNext(); } @Override public boolean checkI18nAndMandatoryPropertiesForLocale(Locale locale) throws RepositoryException { Node i18n = null; if (hasI18N(locale, false)) { i18n = getI18N(locale, false); } for (ExtendedPropertyDefinition def : getPrimaryNodeType().getPropertyDefinitionsAsMap().values()) { if (def.isInternationalized() && def.isMandatory()) { if (i18n == null || !i18n.hasProperty(def.getName())) { return false; } } } return true; } @Override public JCRSiteNode getResolveSite() throws RepositoryException { if (site != null) { return site; } try { String path = getCanonicalPath(); path = path.startsWith("/modulesFileSystem/") ? path.replace("/modulesFileSystem/", "/modules/") : path; if (path.startsWith("/sites/") || path.startsWith("/modules/")) { int index = path.indexOf('/', path.indexOf('/', 1) + 1); if (index == -1) { JCRNodeWrapper node = provider.getNodeWrapper(objectNode, session); if (node instanceof JCRSiteNode) { return (site = (JCRSiteNode) node); } } try { return (site = (JCRSiteNode) (getSession() .getNode(index == -1 ? path : path.substring(0, index)))); } catch (ClassCastException e) { logger.debug("Cannot resolve site for node " + this.getPath(), e); // if node is not a site ( eg ACL / workflow ) } } return (site = (JCRSiteNode) (getSession().getNode(JCRContentUtils.getSystemSitePath()))); } catch (PathNotFoundException e) { logger.debug("Cannot resolve site for node " + this.getPath(), e); } logger.debug("Cannot resolve site for node " + this.getPath()); return null; // return ServicesRegistry.getInstance().getJahiaSitesService().getDefaultSite(); } @Override public String getDisplayableName() { try { if (isNodeType(Constants.JAHIAMIX_RB_TITLE)) { String rb = getProperty(Constants.JAHIA_TITLE_KEY).getValue().getString(); if (rb != null) { return getResourceBundle(rb); } } } catch (RepositoryException e) { logger.debug("Failed to get resourceBundled title", e); } String title = null; try { title = getProperty(Constants.JCR_TITLE).getValue().getString(); } catch (RepositoryException e) { //Search for primary field if present try { String itemName = getPrimaryNodeType().getPrimaryItemName(); if (itemName != null) { String s = getProperty(itemName).getValue().getString(); if (s != null && s.length() > 0) { title = s.contains("<") ? new TextExtractor(new Source(s)).toString() : s; } } } catch (RepositoryException e1) { title = null; } } // also return unescaped name if title is empty if (title != null && !title.isEmpty()) { return (session.getWorkspace().getName().equals(Constants.EDIT_WORKSPACE) && title.contains("##resourceBundle(")) ? interpolateResourceBundle(title) : title; } else { return getUnescapedName(); } } @Override public String getUnescapedName() { String name = getName(); return name != null ? JCRContentUtils.unescapeLocalNodeName(name) : null; } private String interpolateResourceBundle(String title) { Locale locale = getSession().getLocale(); try { JCRSiteNode site = getResolveSite(); for (String module : site.getInstalledModules()) { try { return Messages.interpolateResourceBundleMacro(title, locale != null ? locale : session.getFallbackLocale(), ServicesRegistry.getInstance() .getJahiaTemplateManagerService().getTemplatePackageById(module)); } catch (Exception e) { // ignore return title; } } } catch (RepositoryException e) { logger.warn("Unable to resolve the site for node {}. Cause: {}", getPath(), e.getMessage()); } return title; } private String getResourceBundle(String title) { Locale locale = getSession().getLocale(); try { JCRSiteNode site = getResolveSite(); for (String module : site.getInstalledModules()) { try { String r = Messages.get( null, ServicesRegistry.getInstance().getJahiaTemplateManagerService() .getTemplatePackageById(module), title, locale != null ? locale : session.getFallbackLocale(), null); if (r != null) { return r; } } catch (Exception e) { // ignore } } return title; } catch (RepositoryException e) { logger.warn("Unable to resolve the site for node {}. Cause: {}", getPath(), e.getMessage()); } return title; } public void flushLocalCaches() { hasPropertyCache.clear(); } @Override public boolean canMarkForDeletion() throws RepositoryException { JCRStoreProvider provider = getProvider(); if (!provider.isLockingAvailable() || !provider.isUpdateMixinAvailable()) { return false; } for (String skipType : JCRContentUtils.getInstance().getUnsupportedMarkForDeletionNodeTypes()) { if (isNodeType(skipType)) { return false; } } return true; } @Override public boolean isMarkedForDeletion() throws RepositoryException { return objectNode.isNodeType(JAHIAMIX_MARKED_FOR_DELETION); } @Override public void markForDeletion(String comment) throws RepositoryException { long timer = System.currentTimeMillis(); if (!canMarkForDeletion()) { throw new UnsupportedRepositoryOperationException("Mark for deletion is not supported on this node !"); } checkout(); if (!objectNode.isNodeType(JAHIAMIX_MARKED_FOR_DELETION)) { // no mixin yet, add it addMixin(JAHIAMIX_MARKED_FOR_DELETION); } if (!objectNode.isNodeType(JAHIAMIX_MARKED_FOR_DELETION_ROOT)) { // no mixin for the root node of the deletion yet, add it addMixin(JAHIAMIX_MARKED_FOR_DELETION_ROOT); } // store deletion info: user, date, comment objectNode.setProperty(MARKED_FOR_DELETION_USER, session.getUserID()); objectNode.setProperty(MARKED_FOR_DELETION_DATE, Calendar.getInstance()); if (comment != null && comment.length() > 0) { objectNode.setProperty(MARKED_FOR_DELETION_MESSAGE, comment); } // mark all child nodes as deleted markNodesForDeletion(this); if (session.hasPendingChanges()) { objectNode.getSession().save(); } lockAndStoreToken(MARKED_FOR_DELETION_LOCK_TYPE, MARKED_FOR_DELETION_LOCK_USER); if (logger.isDebugEnabled()) { logger.debug("markForDeletion for node {} took {} ms", getPath(), (System.currentTimeMillis() - timer)); } } private static void markNodesForDeletion(JCRNodeWrapper node) throws RepositoryException { for (NodeIterator iterator = node.getNodes(); iterator.hasNext();) { JCRNodeWrapper child = (JCRNodeWrapper) iterator.nextNode(); if (child.isNodeType(Constants.JAHIANT_TRANSLATION)) { continue; } child.getSession().checkout(child); if (child.isNodeType(JAHIAMIX_MARKED_FOR_DELETION_ROOT)) { // if by any chance the child node was already marked for deletion (root), remove the mixin child.unlock(MARKED_FOR_DELETION_LOCK_TYPE, MARKED_FOR_DELETION_LOCK_USER); child.removeMixin(JAHIAMIX_MARKED_FOR_DELETION_ROOT); } // set mixin if (!child.isNodeType(JAHIAMIX_MARKED_FOR_DELETION)) { child.addMixin(JAHIAMIX_MARKED_FOR_DELETION); } if (child.getSession().hasPendingChanges()) { child.getSession().save(); } // set lock child.lockAndStoreToken(MARKED_FOR_DELETION_LOCK_TYPE, MARKED_FOR_DELETION_LOCK_USER); // recurse into children markNodesForDeletion(child); } } @Override public void unmarkForDeletion() throws RepositoryException { long timer = System.currentTimeMillis(); if (!canMarkForDeletion()) { throw new UnsupportedRepositoryOperationException("Mark for deletion is not supported on this node !"); } checkout(); // remove lock if (isNodeType("jmix:lockable")) { try { unlock(MARKED_FOR_DELETION_LOCK_TYPE, MARKED_FOR_DELETION_LOCK_USER); } catch (LockException ex) { logger.warn("Node {} is not locked. Skipping during undelete operation.", getPath()); } } if (objectNode.isNodeType(JAHIAMIX_MARKED_FOR_DELETION_ROOT)) { removeMixin(JAHIAMIX_MARKED_FOR_DELETION_ROOT); if (objectNode.isNodeType(JAHIAMIX_MARKED_FOR_DELETION)) { removeMixin(JAHIAMIX_MARKED_FOR_DELETION); } // unmark all child nodes unmarkNodesForDeletion(this); } if (logger.isDebugEnabled()) { logger.debug("unmarkForDeletion for node {} took {} ms", getPath(), (System.currentTimeMillis() - timer)); } } private static void unmarkNodesForDeletion(JCRNodeWrapper node) throws RepositoryException { for (NodeIterator iterator = node.getNodes(); iterator.hasNext();) { JCRNodeWrapper child = (JCRNodeWrapper) iterator.nextNode(); if (child.isNodeType(Constants.JAHIANT_TRANSLATION)) { continue; } child.getSession().checkout(child); // do unlock if (child.isNodeType("jmix:lockable")) { try { child.unlock(MARKED_FOR_DELETION_LOCK_TYPE, MARKED_FOR_DELETION_LOCK_USER); } catch (LockException ex) { logger.warn("Node {} is not locked. Skipping during undelete operation.", child.getPath()); } } // remove mixin if (child.isNodeType(JAHIAMIX_MARKED_FOR_DELETION)) { child.removeMixin(JAHIAMIX_MARKED_FOR_DELETION); } // if the child node was before deleted, remove its root mixin if (child.isNodeType(JAHIAMIX_MARKED_FOR_DELETION_ROOT)) { child.removeMixin(JAHIAMIX_MARKED_FOR_DELETION_ROOT); } // recurse into children unmarkNodesForDeletion(child); } } }