package org.onehippo.forge.konakart.cms.replication.utils;
import com.konakart.app.Product;
import com.konakart.appif.ManufacturerIf;
import com.konakartadmin.app.AdminCustomer;
import org.hippoecm.repository.api.*;
import org.hippoecm.repository.standardworkflow.DefaultWorkflow;
import org.hippoecm.repository.standardworkflow.FolderWorkflow;
import org.onehippo.forge.konakart.common.KKCndConstants;
import org.onehippo.forge.konakart.common.util.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.*;
import javax.jcr.version.VersionManager;
import java.rmi.RemoteException;
import java.util.GregorianCalendar;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class NodeHelper {
public static final String PUBLISHED_STATE = "published";
public static final String UNPUBLISHED_STATE = "unpublished";
public static final Logger log = LoggerFactory.getLogger(NodeHelper.class);
/**
* Hippo Repository specific predefined folder node type name
*/
protected String folderNodeTypeName = "hippostd:folder";
/**
* The workflow category name to get a folder workflow. We use threepane as this is the same as the CMS uses
*/
protected String folderNodeWorkflowCategory = "threepane";
/**
* The workflow category name to add a new document.
*/
protected String documentAdditionWorkflowCategory = "new-document";
/**
* The workflow category name to add a new folder.
*/
protected String folderAdditionWorkflowCategory = "new-folder";
/**
* The workflow category name to localize the new document
*/
protected String defaultWorkflowCategory = "core";
protected Session session;
private VersionManager versionManager;
public NodeHelper(Session session) throws RepositoryException {
this.session = session;
versionManager = session.getWorkspace().getVersionManager();
}
public void setFolderNodeTypeName(String folderNodeTypeName) {
this.folderNodeTypeName = folderNodeTypeName;
}
public void setDocumentAdditionWorkflowCategory(String documentAdditionWorkflowCategory) {
this.documentAdditionWorkflowCategory = documentAdditionWorkflowCategory;
}
public void setFolderAdditionWorkflowCategory(String folderAdditionWorkflowCategory) {
this.folderAdditionWorkflowCategory = folderAdditionWorkflowCategory;
}
public Node createMissingFolders(String absPath) throws Exception {
String[] folderNames = absPath.split("/");
Node rootNode = session.getRootNode();
Node curNode = rootNode;
String folderNodePath;
for (String folderName : folderNames) {
String folderNodeName = Codecs.encodeNode(folderName);
if (!"".equals(folderNodeName)) {
if (curNode.equals(rootNode)) {
folderNodePath = "/" + folderNodeName;
} else {
folderNodePath = curNode.getPath() + "/" + folderNodeName;
}
if (!session.itemExists(folderNodePath)) {
curNode = session.getNode(createNodeByWorkflow(curNode, folderNodeTypeName, folderName));
} else {
curNode = curNode.getNode(folderNodeName);
}
if (curNode.isNodeType(HippoNodeType.NT_FACETSELECT) || curNode.isNodeType(HippoNodeType.NT_MIRROR)) {
String docbaseUuid = curNode.getProperty("hippo:docbase").getString();
// check whether docbaseUuid is a valid uuid, otherwise a runtime IllegalArgumentException is thrown
try {
UUID.fromString(docbaseUuid);
} catch (IllegalArgumentException e) {
throw new Exception("hippo:docbase in mirror does not contain a valid uuid", e);
}
// this is always the canonical
curNode = session.getNodeByIdentifier(docbaseUuid);
} else {
curNode = getCanonicalNode(curNode);
}
}
}
return curNode;
}
@SuppressWarnings("rawtypes")
protected String createNodeByWorkflow(Node folderNode, String nodeTypeName, String name) throws Exception {
try {
folderNode = getCanonicalNode(folderNode);
Workflow wf = getWorkflow(folderNodeWorkflowCategory, folderNode);
if (wf instanceof FolderWorkflow) {
FolderWorkflow fwf = (FolderWorkflow) wf;
String category = documentAdditionWorkflowCategory;
if (nodeTypeName.equals(folderNodeTypeName)) {
category = folderAdditionWorkflowCategory;
// now check if there is some more specific workflow for hippostd:folder
if (fwf.hints() != null && fwf.hints().get("prototypes") != null) {
Object protypesMap = fwf.hints().get("prototypes");
if (protypesMap instanceof Map) {
for (Object o : ((Map) protypesMap).entrySet()) {
Map.Entry entry = (Map.Entry) o;
if (entry.getKey() instanceof String && entry.getValue() instanceof Set) {
if (((Set) entry.getValue()).contains(folderNodeTypeName)) {
// we found possibly a more specific workflow for folderNodeTypeName. Use the key as category
category = (String) entry.getKey();
break;
}
}
}
}
}
}
String nodeName = Codecs.encodeNode(name);
String added = fwf.add(category, nodeTypeName, nodeName);
if (added == null) {
throw new Exception("Failed to add document/folder for type '" + nodeTypeName
+ "'. Make sure there is a prototype.");
}
Item addedDocumentVariant = folderNode.getSession().getItem(added);
if (addedDocumentVariant instanceof Node && !nodeName.equals(name)) {
DefaultWorkflow defaultWorkflow = (DefaultWorkflow) getWorkflow(defaultWorkflowCategory, (Node) addedDocumentVariant);
defaultWorkflow.localizeName(name);
}
return added;
} else {
throw new Exception("Can't add folder " + name + " [" + nodeTypeName + "] in the folder " + folderNode.getPath() + ", because there is no FolderWorkflow possible on the folder node: " + wf);
}
} catch (RepositoryException e) {
throw new Exception(e);
} catch (RemoteException e) {
throw new Exception(e);
} catch (WorkflowException e) {
throw new Exception(e);
}
}
public Node createOrRetrieveDocument(Node parentNode, Product product, String docType, String ownerId, String locale) throws RepositoryException {
// Encode the name to be able to add name with special characters
String encodingName = Codecs.encodeNode(product.getName());
if (parentNode.hasNode(encodingName)) {
Node handleNode = parentNode.getNode(encodingName);
if (handleNode.hasNode(encodingName)) {
Node currentNode = handleNode.getNode(encodingName);
// we need to check if two different products have been created using the same name
int productId = (int) currentNode.getProperty(KKCndConstants.PRODUCT_ID).getLong();
if (product.getId() == productId) {
return currentNode;
}
// Not the same id - so a new product will be created
// continue the process used to create a new product's node.
} else {
return null;
}
}
// Create the handle
Node handle = parentNode.addNode(encodingName, "hippo:handle");
handle.addMixin("hippo:hardhandle");
handle.addMixin("hippo:translated");
// Add translation node. This node is used to manager special name
Node translation = handle.addNode("hippo:translation", "hippo:translation");
translation.setProperty("hippo:language", "");
translation.setProperty("hippo:message", product.getName());
// Create the user
Node childNode = handle.addNode(encodingName, docType);
// Add mixin
childNode.addMixin("hippo:harddocument");
childNode.addMixin("hippotranslation:translated");
// Add extra definitions
childNode.setProperty("hippo:availability", new String[]{"live", "preview"});
childNode.setProperty("hippotranslation:id", UUID.randomUUID().toString());
childNode.setProperty("hippotranslation:locale", locale);
childNode.setProperty("hippostdpubwf:lastModifiedBy", "admin");
childNode.setProperty("hippostd:holder", "admin");
childNode.setProperty("hippostdpubwf:lastModificationDate", new GregorianCalendar());
childNode.setProperty("hippostdpubwf:creationDate", new GregorianCalendar());
childNode.setProperty("hippostdpubwf:publicationDate", new GregorianCalendar());
childNode.setProperty("hippostdpubwf:createdBy", ownerId);
return childNode;
}
public Node createOrRetrieveDocument(Node parentNode, ManufacturerIf manufacturer, String docType, String ownerId, String locale) throws RepositoryException {
// Encode the name to be able to add name with special characters
String encodingName = Codecs.encodeNode(manufacturer.getName());
if (parentNode.hasNode(encodingName)) {
Node handleNode = parentNode.getNode(encodingName);
if (handleNode.hasNode(encodingName)) {
return handleNode.getNode(encodingName);
}
return null;
}
// Create the handle
Node handle = parentNode.addNode(encodingName, "hippo:handle");
handle.addMixin("hippo:hardhandle");
handle.addMixin("hippo:translated");
// Add translation node. This node is used to manager special name
Node translation = handle.addNode("hippo:translation", "hippo:translation");
translation.setProperty("hippo:language", "");
translation.setProperty("hippo:message", manufacturer.getName());
// Create the user
Node childNode = handle.addNode(encodingName, docType);
// Add mixin
childNode.addMixin("hippo:harddocument");
childNode.addMixin("hippotranslation:translated");
// Add extra definitions
childNode.setProperty("hippo:availability", new String[]{"live", "preview"});
childNode.setProperty("hippotranslation:id", UUID.randomUUID().toString());
childNode.setProperty("hippotranslation:locale", locale);
childNode.setProperty("hippostdpubwf:lastModifiedBy", "admin");
childNode.setProperty("hippostd:holder", "admin");
childNode.setProperty("hippostdpubwf:lastModificationDate", new GregorianCalendar());
childNode.setProperty("hippostdpubwf:creationDate", new GregorianCalendar());
childNode.setProperty("hippostdpubwf:publicationDate", new GregorianCalendar());
childNode.setProperty("hippostdpubwf:createdBy", ownerId);
return childNode;
}
public Node createOrRetrieveCustomer(AdminCustomer adminCustomer, int dirLevels) throws RepositoryException {
String usersPath = "hippo:configuration/hippo:users";
Node usersNode = session.getRootNode().getNode(usersPath);
String userId = adminCustomer.getEmailAddr();
int length = userId.length();
int pos = 0;
for (int i = 0; i < dirLevels; i++) {
if (i < length) {
pos = i;
}
String c = NodeNameCodec.encode(Character.toLowerCase(userId.charAt(pos)));
if (!usersNode.hasNode(c)) {
usersNode = usersNode.addNode(c, HippoNodeType.NT_USERFOLDER);
} else {
usersNode = usersNode.getNode(c);
}
}
Node user;
if (usersNode.hasNode(userId)) {
user = usersNode.getNode(userId);
} else {
user = usersNode.addNode(userId, "hipposys:user");
}
// Add extra definitions
user.setProperty("hipposys:securityprovider", KKCndConstants.KONAKART_SECURITY_PROVIDER);
user.setProperty("hipposys:active", adminCustomer.isEnabled());
user.setProperty("hipposys:firstname", adminCustomer.getFirstName());
user.setProperty("hipposys:lastname", adminCustomer.getLastName());
user.setProperty("hipposys:email", adminCustomer.getEmailAddr());
user.setProperty("hipposys:password", SecurityUtils.createSyncKonakartPassword());
return usersNode;
}
/**
* Update the hippostd state
*
* @param state the state of the document
*/
public void updateState(Node node, String state) throws RepositoryException {
boolean hasCheckout = false;
// Check if the node is check-in
if (!node.isCheckedOut()) {
checkout(node.getPath());
hasCheckout = true;
}
node.setProperty("hippostd:state", state);
if (state.equals(UNPUBLISHED_STATE)) {
node.setProperty("hippo:availability", new String[]{"preview"});
} else {
node.setProperty("hippo:availability", new String[]{"live", "preview"});
}
if (hasCheckout) {
checkin(node.getPath());
}
}
public String getNodeState(Node node) throws RepositoryException {
if (node.hasProperty("hippostd:state")) {
return node.getProperty("hippostd:state").getString();
}
return null;
}
public Workflow getWorkflow(String category, Node node) throws RepositoryException {
Workspace workspace = session.getWorkspace();
ClassLoader workspaceClassloader = workspace.getClass().getClassLoader();
ClassLoader currentClassloader = Thread.currentThread().getContextClassLoader();
try {
if (!workspaceClassloader.equals(currentClassloader)) {
Thread.currentThread().setContextClassLoader(workspaceClassloader);
}
WorkflowManager wfm = ((HippoWorkspace) workspace).getWorkflowManager();
return wfm.getWorkflow(category, node);
} catch (RepositoryException e) {
throw e;
} catch (Exception e) {
// other exception which are not handled properly in the repository (we cannot do better here then just log them)
if (log.isDebugEnabled()) {
log.warn("Exception in workflow", e);
} else {
log.warn("Exception in workflow: {}", e.toString());
}
} finally {
if (workspaceClassloader.equals(currentClassloader)) {
Thread.currentThread().setContextClassLoader(currentClassloader);
}
}
return null;
}
private Node getCanonicalNode(Node folderNode) {
if (folderNode instanceof HippoNode) {
HippoNode hnode = (HippoNode) folderNode;
try {
Node canonical = hnode.getCanonicalNode();
if (canonical == null) {
log.debug("Cannot get canonical node for '{}'. This means there is no phyiscal equivalence of the " +
"virtual node. Return null", folderNode.getPath());
}
return canonical;
} catch (RepositoryException e) {
throw new RuntimeException(e);
}
}
return folderNode;
}
public void checkout(String path) throws RepositoryException {
versionManager.checkout(path);
}
public void checkin(String path) throws RepositoryException {
versionManager.checkin(path);
}
}