1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.cms.util;
35
36 import info.magnolia.cms.core.Content;
37 import info.magnolia.cms.core.Content.ContentFilter;
38 import info.magnolia.cms.core.DefaultContent;
39 import info.magnolia.cms.core.HierarchyManager;
40 import info.magnolia.cms.core.ItemType;
41 import info.magnolia.cms.core.Path;
42 import info.magnolia.cms.security.AccessDeniedException;
43 import info.magnolia.context.MgnlContext;
44
45 import java.io.File;
46 import java.io.FileInputStream;
47 import java.io.FileOutputStream;
48 import java.io.IOException;
49 import java.util.ArrayList;
50 import java.util.Collection;
51 import java.util.Comparator;
52 import java.util.Iterator;
53 import java.util.List;
54 import java.util.regex.Matcher;
55 import java.util.regex.Pattern;
56
57 import javax.jcr.ImportUUIDBehavior;
58 import javax.jcr.Node;
59 import javax.jcr.PathNotFoundException;
60 import javax.jcr.RepositoryException;
61 import javax.jcr.Session;
62
63 import org.apache.commons.io.FileUtils;
64 import org.apache.commons.io.IOUtils;
65 import org.apache.commons.lang.StringUtils;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69
70
71
72
73
74
75
76 public class ContentUtil {
77 private final static Logger log = LoggerFactory.getLogger(ContentUtil.class);
78
79
80
81
82 public static ContentFilter ALL_NODES_CONTENT_FILTER = new ContentFilter() {
83
84 @Override
85 public boolean accept(Content content) {
86 return true;
87 }
88 };
89
90
91
92
93 public static ContentFilter ALL_NODES_EXCEPT_JCR_CONTENT_FILTER = new ContentFilter() {
94
95 @Override
96 public boolean accept(Content content) {
97 return !content.getName().startsWith("jcr:");
98 }
99 };
100
101
102
103
104 public static ContentFilter EXCLUDE_META_DATA_CONTENT_FILTER = new ContentFilter() {
105 @Override
106 public boolean accept(Content content) {
107 return !content.getName().startsWith("jcr:") && !content.isNodeType(ItemType.NT_METADATA);
108 }
109 };
110
111
112
113
114 public static ContentFilter MAGNOLIA_FILTER = new ContentFilter() {
115 @Override
116 public boolean accept(Content content) {
117
118 try {
119 String nodetype = content.getNodeType().getName();
120
121 return nodetype.startsWith("mgnl:");
122 }
123 catch (RepositoryException e) {
124 log.error("Unable to read nodetype for node {}", content.getHandle());
125 }
126 return false;
127 }
128 };
129
130
131
132
133
134
135 public static interface Visitor {
136 void visit(Content node) throws Exception;
137 }
138
139
140
141
142 public static interface PostVisitor extends Visitor {
143 void postVisit(Content node) throws Exception;
144 }
145
146
147
148
149
150
151 public static Content getContent(String repository, String path) {
152 try {
153 return MgnlContext.getHierarchyManager(repository).getContent(path);
154 }
155 catch (RepositoryException e) {
156 return null;
157 }
158 }
159
160
161
162
163 public static Content getContentByUUID(String repository, String uuid) {
164 try {
165 return MgnlContext.getHierarchyManager(repository).getContentByUUID(uuid);
166 }
167 catch (RepositoryException e) {
168 return null;
169 }
170 }
171
172
173
174
175
176
177
178
179 public static Content getContent(Content node, String name) {
180 try {
181 return node.getContent(name);
182 }
183 catch (RepositoryException e) {
184 return null;
185 }
186 }
187
188
189
190
191 public static Content getOrCreateContent(Content node, String name, ItemType contentType) throws AccessDeniedException, RepositoryException{
192 return getOrCreateContent(node, name, contentType, false);
193 }
194
195
196
197
198 public static Content getOrCreateContent(Content node, String name, ItemType contentType, boolean save)
199 throws AccessDeniedException, RepositoryException {
200 Content res = null;
201 try {
202 res = node.getContent(name);
203 }
204 catch (PathNotFoundException e) {
205 res = node.createContent(name, contentType);
206 if(save){
207 res.getParent().save();
208 }
209 }
210 return res;
211 }
212
213
214
215
216
217
218 public static Content getCaseInsensitive(Content node, String name) {
219 if (name == null || node == null) {
220 return null;
221 }
222 name = name.toLowerCase();
223 for (Content child : node.getChildren(ALL_NODES_CONTENT_FILTER)) {
224 if (child.getName().toLowerCase().equals(name)) {
225 return child;
226 }
227 }
228 return null;
229 }
230
231
232
233
234 public static List<Content> collectAllChildren(Content node) {
235 List<Content> nodes = new ArrayList<Content>();
236 return collectAllChildren(nodes, node, new ItemType[]{ItemType.CONTENT, ItemType.CONTENTNODE});
237 }
238
239
240
241
242
243
244
245 public static List<Content> collectAllChildren(Content node, ContentFilter filter) {
246 List<Content> nodes = new ArrayList<Content>();
247 return collectAllChildren(nodes, node, filter);
248 }
249
250
251
252
253
254 private static List<Content> collectAllChildren(List<Content> nodes, Content node, ContentFilter filter) {
255
256 Collection<Content> children = node.getChildren(filter);
257 for (Content child : children) {
258 nodes.add(child);
259 }
260
261
262 Collection<Content> allChildren = node.getChildren(EXCLUDE_META_DATA_CONTENT_FILTER);
263
264
265 for (Content child : allChildren) {
266 collectAllChildren(nodes, child, filter);
267 }
268
269 return nodes;
270 }
271
272
273
274
275 public static List<Content> collectAllChildren(Content node, ItemType type) {
276 List<Content> nodes = new ArrayList<Content>();
277 return collectAllChildren(nodes, node, new ItemType[]{type});
278 }
279
280
281
282
283 public static Collection<Content> getAllChildren(Content node){
284 return node.getChildren(EXCLUDE_META_DATA_CONTENT_FILTER);
285 }
286
287
288
289
290 public static Collection<Content> getAllChildren(Content node, Comparator<Content> comp){
291 return node.getChildren(EXCLUDE_META_DATA_CONTENT_FILTER, comp);
292 }
293
294
295
296
297 public static List<Content> collectAllChildren(Content node, ItemType[] types) {
298 List<Content> nodes = new ArrayList<Content>();
299 return collectAllChildren(nodes, node, types);
300 }
301
302
303
304
305 private static List<Content> collectAllChildren(List<Content> nodes, Content node, ItemType[] types) {
306 for (int i = 0; i < types.length; i++) {
307 ItemType type = types[i];
308
309 Collection<Content> children = node.getChildren(type);
310 for (Content child : children) {
311 nodes.add(child);
312 collectAllChildren(nodes, child, types);
313 }
314 }
315 return nodes;
316 }
317
318
319
320
321
322 public static Content getAncestorOfType(final Content firstNode, final String nodeType) throws RepositoryException {
323 Content node = firstNode;
324 while (!node.isNodeType(nodeType)) {
325 node = node.getParent();
326 if (node.getLevel() == 0) {
327 throw new RepositoryException("No ancestor of type " + nodeType + " found for " + firstNode);
328 }
329 }
330 return node;
331 }
332
333
334
335
336 public static void orderBefore(Content nodeToMove, String targetNodeName) throws RepositoryException{
337 nodeToMove.getParent().orderBefore(nodeToMove.getName(), targetNodeName);
338 }
339
340
341
342
343 public static void orderAfter(Content nodeToMove, String targetNodeName) throws RepositoryException{
344 Content parent = nodeToMove.getParent();
345 Boolean readyToMove = false;
346
347 Collection<Content> children = new ArrayList<Content>(ContentUtil.getAllChildren(parent));
348 for (Content child : children) {
349 if(readyToMove){
350 parent.orderBefore(nodeToMove.getName(), child.getName());
351 readyToMove = false;
352 break;
353 }
354
355 if(child.getName().equals(targetNodeName)){
356 readyToMove = true;
357 }
358 }
359
360 if(readyToMove){
361 for (Content child : children){
362 if(!nodeToMove.getName().equals(child.getName())){
363 parent.orderBefore(child.getName(), nodeToMove.getName());
364 }
365 }
366 }
367 }
368
369 public static void orderNodes(Content node, String[] nodes) throws RepositoryException{
370 for (int i = nodes.length - 1; i > 0; i--) {
371 node.orderBefore(nodes[i-1], nodes[i]);
372 }
373 }
374
375
376
377
378 public static void orderNodes(Content node, Comparator<Content> comparator) throws RepositoryException {
379 Collection<Content> children = ContentUtil.getAllChildren(node, comparator);
380 String[] names = new String[children.size()];
381
382 int i = 0;
383 for (Content childNode : children) {
384 names[i] = childNode.getName();
385 i++;
386 }
387 orderNodes(node, names);
388 }
389
390 public static void visit(Content node, Visitor visitor) throws Exception{
391 visit(node, visitor, EXCLUDE_META_DATA_CONTENT_FILTER);
392 }
393
394 public static void visit(Content node, Visitor visitor, ContentFilter filter) throws Exception{
395 visitor.visit(node);
396 for (Content content : node.getChildren(filter)) {
397 visit(content, visitor, filter);
398 }
399 if(visitor instanceof PostVisitor){
400 ((PostVisitor)visitor).postVisit(node);
401 }
402 }
403
404 public static Content createPath(HierarchyManager hm, String path) throws AccessDeniedException,
405 PathNotFoundException, RepositoryException {
406 return createPath(hm, path, false);
407 }
408
409 public static Content createPath(HierarchyManager hm, String path, boolean save) throws AccessDeniedException,
410 PathNotFoundException, RepositoryException {
411 return createPath(hm, path, ItemType.CONTENT, save);
412 }
413
414 public static Content createPath(HierarchyManager hm, String path, ItemType type) throws AccessDeniedException,
415 PathNotFoundException, RepositoryException {
416 return createPath(hm, path, type, false);
417 }
418
419 public static Content createPath(HierarchyManager hm, String path, ItemType type, boolean save) throws AccessDeniedException,
420 PathNotFoundException, RepositoryException {
421 Content root = hm.getRoot();
422 return createPath(root, path, type, save);
423 }
424
425 public static Content createPath(Content parent, String path, ItemType type) throws RepositoryException,
426 PathNotFoundException, AccessDeniedException {
427 return createPath(parent, path, type, false);
428 }
429
430 public static Content createPath(Content parent, String path, ItemType type, boolean save) throws RepositoryException,
431 PathNotFoundException, AccessDeniedException {
432
433 path = StringUtils.removeStart(path, "/");
434
435 if (StringUtils.isEmpty(path)) {
436 return parent;
437 }
438
439 String[] names = path.split("/");
440
441 for (int i = 0; i < names.length; i++) {
442 String name = names[i];
443 if (parent.hasContent(name)) {
444 parent = parent.getContent(name);
445 }
446 else {
447 final Content newNode = parent.createContent(name, type);
448 if(save){
449 parent.save();
450 }
451 parent = newNode;
452 }
453 }
454 return parent;
455 }
456
457 public static String uuid2path(String repository, String uuid){
458 if(StringUtils.isNotEmpty(uuid)){
459 HierarchyManager hm = MgnlContext.getHierarchyManager(repository);
460 try {
461 Content node = hm.getContentByUUID(uuid);
462 return node.getHandle();
463 }
464 catch (Exception e) {
465
466 }
467
468 }
469 return uuid;
470 }
471
472 public static String path2uuid(String repository, String path) {
473 if(StringUtils.isNotEmpty(path)){
474 HierarchyManager hm = MgnlContext.getHierarchyManager(repository);
475 try {
476 Content node = hm.getContent(path);
477 return node.getUUID();
478 }
479 catch (Exception e) {
480
481 }
482
483 }
484 return path;
485 }
486
487 public static void deleteAndRemoveEmptyParents(Content node) throws PathNotFoundException, RepositoryException,
488 AccessDeniedException {
489 deleteAndRemoveEmptyParents(node, 0);
490 }
491
492 public static void deleteAndRemoveEmptyParents(Content node, int level) throws PathNotFoundException, RepositoryException,
493 AccessDeniedException {
494 Content parent = null;
495 if(node.getLevel() != 0){
496 parent = node.getParent();
497 }
498 node.delete();
499 if(parent != null && parent.getLevel()>level && parent.getChildren(ContentUtil.EXCLUDE_META_DATA_CONTENT_FILTER).size()==0){
500 deleteAndRemoveEmptyParents(parent, level);
501 }
502 }
503
504
505
506
507
508 public static void copyInSession(Content src, String dest) throws RepositoryException {
509 final String destParentPath = StringUtils.defaultIfEmpty(StringUtils.substringBeforeLast(dest, "/"), "/");
510 final String destNodeName = StringUtils.substringAfterLast(dest, "/");
511 final Session session = src.getWorkspace().getSession();
512 try{
513 final File file = File.createTempFile("mgnl", null, Path.getTempDirectory());
514 final FileOutputStream outStream = new FileOutputStream(file);
515 session.exportSystemView(src.getHandle(), outStream, false, false);
516 outStream.flush();
517 IOUtils.closeQuietly(outStream);
518 FileInputStream inStream = new FileInputStream(file);
519 session.importXML(
520 destParentPath,
521 inStream,
522 ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
523 IOUtils.closeQuietly(inStream);
524 file.delete();
525 if(!StringUtils.equals(src.getName(), destNodeName)){
526 String currentPath;
527 if(destParentPath.equals("/")){
528 currentPath = "/" + src.getName();
529 }
530 else{
531 currentPath = destParentPath + "/" + src.getName();
532 }
533 session.move(currentPath, dest);
534 }
535 }
536 catch (IOException e) {
537 throw new RepositoryException("Can't copy node " + src + " to " + dest, e);
538 }
539 }
540
541
542
543
544 public static void moveInSession(Content src, String dest) throws RepositoryException {
545 src.getWorkspace().getSession().move(src.getHandle(), dest);
546 }
547
548 public static void rename(Content node, String newName) throws RepositoryException{
549 Content parent = node.getParent();
550 String placedBefore = null;
551 for (Iterator<Content> iter = parent.getChildren(node.getNodeTypeName()).iterator(); iter.hasNext();) {
552 Content child = iter.next();
553 if (child.getUUID().equals(node.getUUID())) {
554 if (iter.hasNext()) {
555 child = iter.next();
556 placedBefore = child.getName();
557 }
558 }
559 }
560
561 moveInSession(node, PathUtil.createPath(node.getParent().getHandle(), newName));
562
563
564 if (placedBefore != null) {
565 parent.orderBefore(newName, placedBefore);
566 parent.save();
567 }
568 }
569
570
571
572
573
574
575
576
577
578 public static void changeNodeType(Content node, ItemType newType, boolean replaceAll) throws RepositoryException{
579 if(node == null){
580 throw new IllegalArgumentException("Content can't be null");
581 }
582 if(newType == null){
583 throw new IllegalArgumentException("ItemType can't be null");
584 }
585 final String oldTypeName = node.getNodeTypeName();
586 final String newTypeName = newType.getSystemName();
587 if(newTypeName.equals(oldTypeName)){
588 log.info("Old node type and new one are the same {}. Nothing to change.", newTypeName);
589 return;
590 }
591 final Pattern nodeTypePattern = Pattern.compile("(<sv:property\\s+sv:name=\"jcr:primaryType\"\\s+sv:type=\"Name\"><sv:value>)("+oldTypeName+")(</sv:value>)");
592 final String replacement = "$1"+newTypeName+"$3";
593
594 log.debug("pattern is {}", nodeTypePattern.pattern());
595 log.debug("replacement string is {}", replacement);
596 log.debug("replaceAll? {}", replaceAll);
597
598 final String destParentPath = StringUtils.defaultIfEmpty(StringUtils.substringBeforeLast(node.getHandle(), "/"), "/");
599 final Session session = node.getWorkspace().getSession();
600 FileOutputStream outStream = null;
601 FileInputStream inStream = null;
602 File file = null;
603
604 try {
605 file = File.createTempFile("mgnl", null, Path.getTempDirectory());
606 outStream = new FileOutputStream(file);
607 session.exportSystemView(node.getHandle(), outStream, false, false);
608 outStream.flush();
609 final String fileContents = FileUtils.readFileToString(file);
610 log.debug("content string is {}", fileContents);
611 final Matcher matcher = nodeTypePattern.matcher(fileContents);
612 String replaced = null;
613
614 log.debug("starting find&replace...");
615 long start = System.currentTimeMillis();
616 if(matcher.find()) {
617 log.debug("{} will be replaced", node.getHandle());
618 if(replaceAll){
619 replaced = matcher.replaceAll(replacement);
620 } else {
621 replaced = matcher.replaceFirst(replacement);
622 }
623 log.debug("replaced string is {}", replaced);
624 } else {
625 log.debug("{} won't be replaced", node.getHandle());
626 return;
627 }
628 log.debug("find&replace operations took {}ms", (System.currentTimeMillis() - start));
629
630 FileUtils.writeStringToFile(file, replaced);
631 inStream = new FileInputStream(file);
632 session.importXML(
633 destParentPath,
634 inStream,
635 ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
636
637 } catch (IOException e) {
638 throw new RepositoryException("Can't replace node " + node.getHandle(), e);
639 } finally {
640 IOUtils.closeQuietly(outStream);
641 IOUtils.closeQuietly(inStream);
642 FileUtils.deleteQuietly(file);
643 }
644 }
645
646 public static Content asContent(Node content) {
647 if(content == null) {
648 return null;
649 }
650 return new DefaultContent(content);
651 }
652 }