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
71 public class ContentUtil {
72 private final static Logger log = LoggerFactory.getLogger(ContentUtil.class);
73
74
75
76
77 public static ContentFilter ALL_NODES_CONTENT_FILTER = new ContentFilter() {
78
79 public boolean accept(Content content) {
80 return true;
81 }
82 };
83
84
85
86
87 public static ContentFilter ALL_NODES_EXCEPT_JCR_CONTENT_FILTER = new ContentFilter() {
88
89 public boolean accept(Content content) {
90 return !content.getName().startsWith("jcr:");
91 }
92 };
93
94
95
96
97 public static ContentFilter EXCLUDE_META_DATA_CONTENT_FILTER = new ContentFilter() {
98 public boolean accept(Content content) {
99 return !content.getName().startsWith("jcr:") && !content.isNodeType(ItemType.NT_METADATA);
100 }
101 };
102
103
104
105
106 public static ContentFilter MAGNOLIA_FILTER = new ContentFilter() {
107 public boolean accept(Content content) {
108
109 try {
110 String nodetype = content.getNodeType().getName();
111
112 return nodetype.startsWith("mgnl:");
113 }
114 catch (RepositoryException e) {
115 log.error("Unable to read nodetype for node {}", content.getHandle());
116 }
117 return false;
118 }
119 };
120
121
122
123
124
125
126 public static interface Visitor {
127 void visit(Content node) throws Exception;
128 }
129
130 public static interface PostVisitor extends Visitor {
131 void postVisit(Content node) throws Exception;
132 }
133
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
246
247
248 private static List<Content> collectAllChildren(List<Content> nodes, Content node, ContentFilter filter) {
249
250 Collection<Content> children = node.getChildren(filter);
251 for (Content child : children) {
252 nodes.add(child);
253 }
254
255
256 Collection<Content> allChildren = node.getChildren(EXCLUDE_META_DATA_CONTENT_FILTER);
257
258
259 for (Content child : allChildren) {
260 collectAllChildren(nodes, child, filter);
261 }
262
263 return nodes;
264 }
265
266
267
268
269
270
271
272 public static List<Content> collectAllChildren(Content node, ItemType type) {
273 List<Content> nodes = new ArrayList<Content>();
274 return collectAllChildren(nodes, node, new ItemType[]{type});
275 }
276
277
278
279
280 public static Collection<Content> getAllChildren(Content node){
281 return node.getChildren(EXCLUDE_META_DATA_CONTENT_FILTER);
282 }
283
284
285
286
287 public static Collection<Content> getAllChildren(Content node, Comparator<Content> comp){
288 return node.getChildren(EXCLUDE_META_DATA_CONTENT_FILTER, comp);
289 }
290
291
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
306
307
308
309 private static List<Content> collectAllChildren(List<Content> nodes, Content node, ItemType[] types) {
310 for (int i = 0; i < types.length; i++) {
311 ItemType type = types[i];
312
313 Collection<Content> children = node.getChildren(type);
314 for (Content child : children) {
315 nodes.add(child);
316 collectAllChildren(nodes, child, types);
317 }
318 }
319 return nodes;
320 }
321
322
323
324
325 public static void orderBefore(Content nodeToMove, String targetNodeName) throws RepositoryException{
326 nodeToMove.getParent().orderBefore(nodeToMove.getName(), targetNodeName);
327 }
328
329
330
331
332 public static void orderAfter(Content nodeToMove, String targetNodeName) throws RepositoryException{
333 Content parent = nodeToMove.getParent();
334
335 Collection<Content> children = new ArrayList<Content>(ContentUtil.getAllChildren(parent));
336
337
338 for (Content child : children) {
339
340 if(child.getName().equals(nodeToMove.getName())){
341 continue;
342 }
343
344 parent.orderBefore(child.getName(), nodeToMove.getName());
345 if(child.getName().equals(targetNodeName)){
346 break;
347 }
348 }
349 }
350
351 public static void orderNodes(Content node, String[] nodes) throws RepositoryException{
352 for (int i = nodes.length - 1; i > 0; i--) {
353 node.orderBefore(nodes[i-1], nodes[i]);
354 }
355 }
356
357
358
359
360
361 public static void orderNodes(Content node, Comparator<Content> comparator) throws RepositoryException {
362 Collection<Content> children = ContentUtil.getAllChildren(node, comparator);
363 String[] names = new String[children.size()];
364
365 int i = 0;
366 for (Content childNode : children) {
367 names[i] = childNode.getName();
368 i++;
369 }
370 orderNodes(node, names);
371 }
372
373 public static void visit(Content node, Visitor visitor) throws Exception{
374 visit(node, visitor, EXCLUDE_META_DATA_CONTENT_FILTER);
375 }
376
377 public static void visit(Content node, Visitor visitor, ContentFilter filter) throws Exception{
378 visitor.visit(node);
379 for (Content content : node.getChildren(filter)) {
380 visit(content, visitor, filter);
381 }
382 if(visitor instanceof PostVisitor){
383 ((PostVisitor)visitor).postVisit(node);
384 }
385 }
386
387 public static Content createPath(HierarchyManager hm, String path) throws AccessDeniedException,
388 PathNotFoundException, RepositoryException {
389 return createPath(hm, path, false);
390 }
391
392 public static Content createPath(HierarchyManager hm, String path, boolean save) throws AccessDeniedException,
393 PathNotFoundException, RepositoryException {
394 return createPath(hm, path, ItemType.CONTENT, save);
395 }
396
397 public static Content createPath(HierarchyManager hm, String path, ItemType type) throws AccessDeniedException,
398 PathNotFoundException, RepositoryException {
399 return createPath(hm, path, type, false);
400 }
401
402 public static Content createPath(HierarchyManager hm, String path, ItemType type, boolean save) throws AccessDeniedException,
403 PathNotFoundException, RepositoryException {
404 Content root = hm.getRoot();
405 return createPath(root, path, type, save);
406 }
407
408 public static Content createPath(Content parent, String path, ItemType type) throws RepositoryException,
409 PathNotFoundException, AccessDeniedException {
410 return createPath(parent, path, type, false);
411 }
412
413 public static Content createPath(Content parent, String path, ItemType type, boolean save) throws RepositoryException,
414 PathNotFoundException, AccessDeniedException {
415
416 path = StringUtils.removeStart(path, "/");
417
418 if (StringUtils.isEmpty(path)) {
419 return parent;
420 }
421
422 String[] names = path.split("/");
423
424 for (int i = 0; i < names.length; i++) {
425 String name = names[i];
426 if (parent.hasContent(name)) {
427 parent = parent.getContent(name);
428 }
429 else {
430 final Content newNode = parent.createContent(name, type);
431 if(save){
432 parent.save();
433 }
434 parent = newNode;
435 }
436 }
437 return parent;
438 }
439
440 public static String uuid2path(String repository, String uuid){
441 if(StringUtils.isNotEmpty(uuid)){
442 HierarchyManager hm = MgnlContext.getHierarchyManager(repository);
443 try {
444 Content node = hm.getContentByUUID(uuid);
445 return node.getHandle();
446 }
447 catch (Exception e) {
448
449 }
450
451 }
452 return uuid;
453 }
454
455 public static String path2uuid(String repository, String path) {
456 if(StringUtils.isNotEmpty(path)){
457 HierarchyManager hm = MgnlContext.getHierarchyManager(repository);
458 try {
459 Content node = hm.getContent(path);
460 return node.getUUID();
461 }
462 catch (Exception e) {
463
464 }
465
466 }
467 return path;
468 }
469
470 public static void deleteAndRemoveEmptyParents(Content node) throws PathNotFoundException, RepositoryException,
471 AccessDeniedException {
472 deleteAndRemoveEmptyParents(node, 0);
473 }
474
475 public static void deleteAndRemoveEmptyParents(Content node, int level) throws PathNotFoundException, RepositoryException,
476 AccessDeniedException {
477 Content parent = null;
478 if(node.getLevel() != 0){
479 parent = node.getParent();
480 }
481 node.delete();
482 if(parent != null && parent.getLevel()>level && parent.getChildren(ContentUtil.EXCLUDE_META_DATA_CONTENT_FILTER).size()==0){
483 deleteAndRemoveEmptyParents(parent, level);
484 }
485 }
486
487
488
489
490
491 public static void copyInSession(Content src, String dest) throws RepositoryException {
492 final String destParentPath = StringUtils.defaultIfEmpty(StringUtils.substringBeforeLast(dest, "/"), "/");
493 final String destNodeName = StringUtils.substringAfterLast(dest, "/");
494 final Session session = src.getWorkspace().getSession();
495 try{
496 final File file = File.createTempFile("mgnl", null, Path.getTempDirectory());
497 final FileOutputStream outStream = new FileOutputStream(file);
498 session.exportSystemView(src.getHandle(), outStream, false, false);
499 outStream.flush();
500 IOUtils.closeQuietly(outStream);
501 FileInputStream inStream = new FileInputStream(file);
502 session.importXML(
503 destParentPath,
504 inStream,
505 ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
506 IOUtils.closeQuietly(inStream);
507 file.delete();
508 if(!StringUtils.equals(src.getName(), destNodeName)){
509 String currentPath;
510 if(destParentPath.equals("/")){
511 currentPath = "/" + src.getName();
512 }
513 else{
514 currentPath = destParentPath + "/" + src.getName();
515 }
516 session.move(currentPath, dest);
517 }
518 }
519 catch (IOException e) {
520 throw new RepositoryException("Can't copy node " + src + " to " + dest, e);
521 }
522 }
523
524
525
526
527 public static void moveInSession(Content src, String dest) throws RepositoryException {
528 src.getWorkspace().getSession().move(src.getHandle(), dest);
529 }
530
531 public static void rename(Content node, String newName) throws RepositoryException{
532 Content parent = node.getParent();
533 String placedBefore = null;
534 for (Iterator<Content> iter = parent.getChildren(node.getNodeTypeName()).iterator(); iter.hasNext();) {
535 Content child = iter.next();
536 if (child.getUUID().equals(node.getUUID())) {
537 if (iter.hasNext()) {
538 child = iter.next();
539 placedBefore = child.getName();
540 }
541 }
542 }
543
544 moveInSession(node, PathUtil.createPath(node.getParent().getHandle(), newName));
545
546
547 if (placedBefore != null) {
548 parent.orderBefore(newName, placedBefore);
549 parent.save();
550 }
551 }
552
553
554
555
556
557
558
559
560
561 public static void changeNodeType(Content node, ItemType newType, boolean replaceAll) throws RepositoryException{
562 if(node == null){
563 throw new IllegalArgumentException("Content can't be null");
564 }
565 if(newType == null){
566 throw new IllegalArgumentException("ItemType can't be null");
567 }
568 final String oldTypeName = node.getNodeTypeName();
569 final String newTypeName = newType.getSystemName();
570 if(newTypeName.equals(oldTypeName)){
571 log.info("Old node type and new one are the same {}. Nothing to change.", newTypeName);
572 return;
573 }
574 final Pattern nodeTypePattern = Pattern.compile("(<sv:property\\s+sv:name=\"jcr:primaryType\"\\s+sv:type=\"Name\"><sv:value>)("+oldTypeName+")(</sv:value>)");
575 final String replacement = "$1"+newTypeName+"$3";
576
577 log.debug("pattern is {}", nodeTypePattern.pattern());
578 log.debug("replacement string is {}", replacement);
579 log.debug("replaceAll? {}", replaceAll);
580
581 final String destParentPath = StringUtils.defaultIfEmpty(StringUtils.substringBeforeLast(node.getHandle(), "/"), "/");
582 final Session session = node.getWorkspace().getSession();
583 FileOutputStream outStream = null;
584 FileInputStream inStream = null;
585 File file = null;
586
587 try {
588 file = File.createTempFile("mgnl", null, Path.getTempDirectory());
589 outStream = new FileOutputStream(file);
590 session.exportSystemView(node.getHandle(), outStream, false, false);
591 outStream.flush();
592 final String fileContents = FileUtils.readFileToString(file);
593 log.debug("content string is {}", fileContents);
594 final Matcher matcher = nodeTypePattern.matcher(fileContents);
595 String replaced = null;
596
597 log.debug("starting find&replace...");
598 long start = System.currentTimeMillis();
599 if(matcher.find()) {
600 log.debug("{} will be replaced", node.getHandle());
601 if(replaceAll){
602 replaced = matcher.replaceAll(replacement);
603 } else {
604 replaced = matcher.replaceFirst(replacement);
605 }
606 log.debug("replaced string is {}", replaced);
607 } else {
608 log.debug("{} won't be replaced", node.getHandle());
609 return;
610 }
611 log.debug("find&replace operations took {}ms" + (System.currentTimeMillis() - start) / 1000);
612
613 FileUtils.writeStringToFile(file, replaced);
614 inStream = new FileInputStream(file);
615 session.importXML(
616 destParentPath,
617 inStream,
618 ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
619
620 } catch (IOException e) {
621 throw new RepositoryException("Can't replace node " + node.getHandle(), e);
622 } finally {
623 IOUtils.closeQuietly(outStream);
624 IOUtils.closeQuietly(inStream);
625 FileUtils.deleteQuietly(file);
626 }
627 }
628 }