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