1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.onehippo.forge.channelmanager.pagesupport.document.management.impl;
17
18 import java.rmi.RemoteException;
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.Map.Entry;
22 import java.util.Set;
23
24 import javax.jcr.ItemNotFoundException;
25 import javax.jcr.Node;
26 import javax.jcr.NodeIterator;
27 import javax.jcr.RepositoryException;
28 import javax.jcr.Session;
29 import javax.jcr.Workspace;
30
31 import org.apache.commons.lang3.StringUtils;
32 import org.hippoecm.repository.HippoStdNodeType;
33 import org.hippoecm.repository.api.HippoNode;
34 import org.hippoecm.repository.api.HippoNodeType;
35 import org.hippoecm.repository.api.HippoWorkspace;
36 import org.hippoecm.repository.api.StringCodec;
37 import org.hippoecm.repository.api.StringCodecFactory;
38 import org.hippoecm.repository.api.Workflow;
39 import org.hippoecm.repository.api.WorkflowException;
40 import org.hippoecm.repository.api.WorkflowManager;
41 import org.hippoecm.repository.standardworkflow.DefaultWorkflow;
42 import org.hippoecm.repository.standardworkflow.FolderWorkflow;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46
47
48
49 class HippoWorkflowUtils {
50
51 private static final Logger log = LoggerFactory.getLogger(HippoWorkflowUtils.class);
52
53
54
55
56 private static final String DEFAULT_HIPPO_FOLDER_NODE_TYPE = "hippostd:folder";
57
58
59
60
61 private static final String DEFAULT_HIPPO_FOLDER_WORKFLOW_CATEGORY = "threepane";
62
63
64
65
66 private static final String DEFAULT_NEW_DOCUMENT_WORKFLOW_CATEGORY = "new-document";
67
68
69
70
71 private static final String DEFAULT_NEW_FOLDER_WORKFLOW_CATEGORY = "new-folder";
72
73
74
75
76 private static final String DEFAULT_WORKFLOW_CATEGORY = "core";
77
78
79
80
81 private static final StringCodec DEFAULT_URI_ENCODING = new StringCodecFactory.UriEncoding();
82
83 private HippoWorkflowUtils() {
84 }
85
86
87
88
89
90
91
92
93
94 public static Workflow getHippoWorkflow(final Session session, final String category, final Node node)
95 throws RepositoryException {
96 Workspace workspace = session.getWorkspace();
97
98 ClassLoader workspaceClassloader = workspace.getClass().getClassLoader();
99 ClassLoader currentClassloader = Thread.currentThread().getContextClassLoader();
100
101 try {
102 if (workspaceClassloader != currentClassloader) {
103 Thread.currentThread().setContextClassLoader(workspaceClassloader);
104 }
105
106 WorkflowManager wfm = ((HippoWorkspace) workspace).getWorkflowManager();
107
108 return wfm.getWorkflow(category, node);
109 } finally {
110 if (workspaceClassloader != currentClassloader) {
111 Thread.currentThread().setContextClassLoader(currentClassloader);
112 }
113 }
114 }
115
116
117
118
119
120
121
122 public static Map<String, Node> getDocumentVariantsMap(final Node handle) throws RepositoryException {
123 Map<String, Node> variantsMap = new HashMap<>();
124 Node variantNode = null;
125 String hippoState;
126
127 for (NodeIterator nodeIt = handle.getNodes(handle.getName()); nodeIt.hasNext(); ) {
128 variantNode = nodeIt.nextNode();
129
130 if (variantNode.hasProperty(HippoStdNodeType.HIPPOSTD_STATE)) {
131 hippoState = variantNode.getProperty(HippoStdNodeType.HIPPOSTD_STATE).getString();
132 variantsMap.put(hippoState, variantNode);
133 }
134 }
135
136 return variantsMap;
137 }
138
139
140
141
142
143
144
145
146
147 public static Node createMissingHippoFolders(final Session session, String absPath)
148 throws RepositoryException, WorkflowException {
149 String[] folderNames = StringUtils.split(absPath, "/");
150
151 Node rootNode = session.getRootNode();
152 Node curNode = rootNode;
153 String folderNodePath;
154
155 for (String folderName : folderNames) {
156 String folderNodeName = DEFAULT_URI_ENCODING.encode(folderName);
157
158 if (curNode == rootNode) {
159 folderNodePath = "/" + folderNodeName;
160 } else {
161 folderNodePath = curNode.getPath() + "/" + folderNodeName;
162 }
163
164 Node existingFolderNode = getExistingHippoFolderNode(session, folderNodePath);
165
166 if (existingFolderNode == null) {
167 curNode = session
168 .getNode(createHippoFolderNodeByWorkflow(session, curNode, DEFAULT_HIPPO_FOLDER_NODE_TYPE, folderName));
169 } else {
170 curNode = existingFolderNode;
171 }
172
173 curNode = getHippoCanonicalNode(curNode);
174
175 if (isHippoMirrorNode(curNode)) {
176 curNode = getRereferencedNodeByHippoMirror(curNode);
177 }
178 }
179
180 return curNode;
181 }
182
183
184
185
186
187
188
189
190 public static Node getHippoDocumentHandle(Node node) throws RepositoryException {
191 if (node.isNodeType("hippo:handle")) {
192 return node;
193 } else if (node.isNodeType("hippo:document")) {
194 if (!node.getSession().getRootNode().isSame(node)) {
195 Node parentNode = node.getParent();
196
197 if (parentNode.isNodeType("hippo:handle")) {
198 return parentNode;
199 }
200 }
201 }
202
203 return null;
204 }
205
206 private static Node getHippoCanonicalNode(Node node) {
207 if (node instanceof HippoNode) {
208 HippoNode hnode = (HippoNode) node;
209
210 try {
211 Node canonical = hnode.getCanonicalNode();
212
213 if (canonical == null) {
214 log.debug("Cannot get canonical node for '{}'. This means there is no phyiscal equivalence of the "
215 + "virtual node. Return null", node.getPath());
216 }
217
218 return canonical;
219 } catch (RepositoryException e) {
220 log.error("Repository exception while fetching canonical node. Return null", e);
221 throw new RuntimeException(e);
222 }
223 }
224
225 return node;
226 }
227
228 private static boolean isHippoMirrorNode(Node node) throws RepositoryException {
229 if (node.isNodeType(HippoNodeType.NT_FACETSELECT) || node.isNodeType(HippoNodeType.NT_MIRROR)) {
230 return true;
231 }
232
233 return false;
234 }
235
236 private static Node getRereferencedNodeByHippoMirror(Node mirrorNode) {
237 String docBaseUUID = null;
238
239 try {
240 if (!isHippoMirrorNode(mirrorNode)) {
241 log.info("Cannot deref a node that is not of (sub)type '{}' or '{}'. Return null",
242 HippoNodeType.NT_FACETSELECT, HippoNodeType.NT_MIRROR);
243 return null;
244 }
245
246
247 docBaseUUID = mirrorNode.getProperty(HippoNodeType.HIPPO_DOCBASE).getString();
248
249 try {
250 return mirrorNode.getSession().getNodeByIdentifier(docBaseUUID);
251 } catch (IllegalArgumentException e) {
252 log.warn("Docbase cannot be parsed to a valid uuid. Return null");
253 return null;
254 }
255 } catch (ItemNotFoundException e) {
256 String path = null;
257
258 try {
259 path = mirrorNode.getPath();
260 } catch (RepositoryException e1) {
261 log.error("RepositoryException, cannot return deferenced node: {}", e1);
262 }
263
264 log.info(
265 "ItemNotFoundException, cannot return deferenced node because docbase uuid '{}' cannot be found. The docbase property is at '{}/hippo:docbase'. Return null",
266 docBaseUUID, path);
267 } catch (RepositoryException e) {
268 log.error("RepositoryException, cannot return deferenced node: {}", e);
269 }
270
271 return null;
272 }
273
274 private static Node getExistingHippoFolderNode(final Session session, final String absPath)
275 throws RepositoryException {
276 if (!session.nodeExists(absPath)) {
277 return null;
278 }
279
280 Node node = session.getNode(absPath);
281 Node candidateNode = null;
282
283 if (session.getRootNode().isSame(node)) {
284 return session.getRootNode();
285 } else {
286 Node parentNode = node.getParent();
287 for (NodeIterator nodeIt = parentNode.getNodes(node.getName()); nodeIt.hasNext();) {
288 Node siblingNode = nodeIt.nextNode();
289 if (!isHippoDocumentHandleOrVariant(siblingNode)) {
290 candidateNode = siblingNode;
291 break;
292 }
293 }
294 }
295
296 if (candidateNode == null) {
297 return null;
298 }
299
300 Node canonicalFolderNode = getHippoCanonicalNode(candidateNode);
301
302 if (isHippoMirrorNode(canonicalFolderNode)) {
303 canonicalFolderNode = getRereferencedNodeByHippoMirror(canonicalFolderNode);
304 }
305
306 if (canonicalFolderNode == null) {
307 return null;
308 }
309
310 if (isHippoDocumentHandleOrVariant(canonicalFolderNode)) {
311 return null;
312 }
313
314 return canonicalFolderNode;
315 }
316
317 private static boolean isHippoDocumentHandleOrVariant(Node node) throws RepositoryException {
318 if (node.isNodeType("hippo:handle")) {
319 return true;
320 } else if (node.isNodeType("hippo:document")) {
321 if (!node.getSession().getRootNode().isSame(node)) {
322 Node parentNode = node.getParent();
323
324 if (parentNode.isNodeType("hippo:handle")) {
325 return true;
326 }
327 }
328 }
329
330 return false;
331 }
332
333 private static String createHippoFolderNodeByWorkflow(final Session session, Node folderNode, String nodeTypeName,
334 String name) throws RepositoryException, WorkflowException {
335 try {
336 folderNode = getHippoCanonicalNode(folderNode);
337 Workflow wf = getHippoWorkflow(session, DEFAULT_HIPPO_FOLDER_WORKFLOW_CATEGORY, folderNode);
338
339 if (wf instanceof FolderWorkflow) {
340 FolderWorkflow fwf = (FolderWorkflow) wf;
341
342 String category = DEFAULT_NEW_DOCUMENT_WORKFLOW_CATEGORY;
343
344 if (nodeTypeName.equals(DEFAULT_HIPPO_FOLDER_NODE_TYPE)) {
345 category = DEFAULT_NEW_FOLDER_WORKFLOW_CATEGORY;
346
347
348 if (fwf.hints() != null && fwf.hints().get("prototypes") != null) {
349 Object protypesMap = fwf.hints().get("prototypes");
350 if (protypesMap instanceof Map) {
351 for (Object o : ((Map) protypesMap).entrySet()) {
352 Entry entry = (Entry) o;
353 if (entry.getKey() instanceof String && entry.getValue() instanceof Set) {
354 if (((Set) entry.getValue()).contains(DEFAULT_HIPPO_FOLDER_NODE_TYPE)) {
355
356 category = (String) entry.getKey();
357 break;
358 }
359 }
360 }
361 }
362 }
363 }
364
365 String nodeName = DEFAULT_URI_ENCODING.encode(name);
366 String added = fwf.add(category, nodeTypeName, nodeName);
367 if (added == null) {
368 throw new WorkflowException("Failed to add document/folder for type '" + nodeTypeName
369 + "'. Make sure there is a prototype.");
370 }
371 Node addedNode = folderNode.getSession().getNode(added);
372 if (!nodeName.equals(name)) {
373 DefaultWorkflow defaultWorkflow = (DefaultWorkflow) getHippoWorkflow(session, DEFAULT_WORKFLOW_CATEGORY,
374 addedNode);
375 defaultWorkflow.setDisplayName(name);
376 }
377
378 if (DEFAULT_NEW_DOCUMENT_WORKFLOW_CATEGORY.equals(category)) {
379
380
381 if (addedNode.isNodeType("hippostd:publishable")) {
382 log.info("Added document '{}' is pusblishable so set status to preview.", addedNode.getPath());
383 addedNode.setProperty("hippostd:state", "unpublished");
384 addedNode.setProperty(HippoNodeType.HIPPO_AVAILABILITY, new String[] { "preview" });
385 } else {
386 log.info("Added document '{}' is not publishable so set status to live & preview directly.",
387 addedNode.getPath());
388 addedNode.setProperty(HippoNodeType.HIPPO_AVAILABILITY, new String[] { "live", "preview" });
389 }
390
391 if (addedNode.isNodeType("hippostd:publishableSummary")) {
392 addedNode.setProperty("hippostd:stateSummary", "new");
393 }
394 addedNode.getSession().save();
395 }
396 return added;
397 } else {
398 throw new WorkflowException(
399 "Can't create folder " + name + " [" + nodeTypeName + "] in the folder " + folderNode.getPath()
400 + ", because there is no FolderWorkflow possible on the folder node: " + wf);
401 }
402 } catch (RemoteException e) {
403 throw new WorkflowException(e.toString(), e);
404 }
405 }
406 }