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 public static void orderBefore(Content nodeToMove, String targetNodeName) throws RepositoryException{
313 nodeToMove.getParent().orderBefore(nodeToMove.getName(), targetNodeName);
314 }
315
316
317
318
319 public static void orderAfter(Content nodeToMove, String targetNodeName) throws RepositoryException{
320 Content parent = nodeToMove.getParent();
321
322 Collection<Content> children = new ArrayList<Content>(ContentUtil.getAllChildren(parent));
323
324
325 for (Content child : children) {
326
327 if(child.getName().equals(nodeToMove.getName())){
328 continue;
329 }
330
331 parent.orderBefore(child.getName(), nodeToMove.getName());
332 if(child.getName().equals(targetNodeName)){
333 break;
334 }
335 }
336 }
337
338 public static void orderNodes(Content node, String[] nodes) throws RepositoryException{
339 for (int i = nodes.length - 1; i > 0; i--) {
340 node.orderBefore(nodes[i-1], nodes[i]);
341 }
342 }
343
344
345
346
347 public static void orderNodes(Content node, Comparator<Content> comparator) throws RepositoryException {
348 Collection<Content> children = ContentUtil.getAllChildren(node, comparator);
349 String[] names = new String[children.size()];
350
351 int i = 0;
352 for (Content childNode : children) {
353 names[i] = childNode.getName();
354 i++;
355 }
356 orderNodes(node, names);
357 }
358
359 public static void visit(Content node, Visitor visitor) throws Exception{
360 visit(node, visitor, EXCLUDE_META_DATA_CONTENT_FILTER);
361 }
362
363 public static void visit(Content node, Visitor visitor, ContentFilter filter) throws Exception{
364 visitor.visit(node);
365 for (Content content : node.getChildren(filter)) {
366 visit(content, visitor, filter);
367 }
368 if(visitor instanceof PostVisitor){
369 ((PostVisitor)visitor).postVisit(node);
370 }
371 }
372
373 public static Content createPath(HierarchyManager hm, String path) throws AccessDeniedException,
374 PathNotFoundException, RepositoryException {
375 return createPath(hm, path, false);
376 }
377
378 public static Content createPath(HierarchyManager hm, String path, boolean save) throws AccessDeniedException,
379 PathNotFoundException, RepositoryException {
380 return createPath(hm, path, ItemType.CONTENT, save);
381 }
382
383 public static Content createPath(HierarchyManager hm, String path, ItemType type) throws AccessDeniedException,
384 PathNotFoundException, RepositoryException {
385 return createPath(hm, path, type, false);
386 }
387
388 public static Content createPath(HierarchyManager hm, String path, ItemType type, boolean save) throws AccessDeniedException,
389 PathNotFoundException, RepositoryException {
390 Content root = hm.getRoot();
391 return createPath(root, path, type, save);
392 }
393
394 public static Content createPath(Content parent, String path, ItemType type) throws RepositoryException,
395 PathNotFoundException, AccessDeniedException {
396 return createPath(parent, path, type, false);
397 }
398
399 public static Content createPath(Content parent, String path, ItemType type, boolean save) throws RepositoryException,
400 PathNotFoundException, AccessDeniedException {
401
402 path = StringUtils.removeStart(path, "/");
403
404 if (StringUtils.isEmpty(path)) {
405 return parent;
406 }
407
408 String[] names = path.split("/");
409
410 for (int i = 0; i < names.length; i++) {
411 String name = names[i];
412 if (parent.hasContent(name)) {
413 parent = parent.getContent(name);
414 }
415 else {
416 final Content newNode = parent.createContent(name, type);
417 if(save){
418 parent.save();
419 }
420 parent = newNode;
421 }
422 }
423 return parent;
424 }
425
426 public static String uuid2path(String repository, String uuid){
427 if(StringUtils.isNotEmpty(uuid)){
428 HierarchyManager hm = MgnlContext.getHierarchyManager(repository);
429 try {
430 Content node = hm.getContentByUUID(uuid);
431 return node.getHandle();
432 }
433 catch (Exception e) {
434
435 }
436
437 }
438 return uuid;
439 }
440
441 public static String path2uuid(String repository, String path) {
442 if(StringUtils.isNotEmpty(path)){
443 HierarchyManager hm = MgnlContext.getHierarchyManager(repository);
444 try {
445 Content node = hm.getContent(path);
446 return node.getUUID();
447 }
448 catch (Exception e) {
449
450 }
451
452 }
453 return path;
454 }
455
456 public static void deleteAndRemoveEmptyParents(Content node) throws PathNotFoundException, RepositoryException,
457 AccessDeniedException {
458 deleteAndRemoveEmptyParents(node, 0);
459 }
460
461 public static void deleteAndRemoveEmptyParents(Content node, int level) throws PathNotFoundException, RepositoryException,
462 AccessDeniedException {
463 Content parent = null;
464 if(node.getLevel() != 0){
465 parent = node.getParent();
466 }
467 node.delete();
468 if(parent != null && parent.getLevel()>level && parent.getChildren(ContentUtil.EXCLUDE_META_DATA_CONTENT_FILTER).size()==0){
469 deleteAndRemoveEmptyParents(parent, level);
470 }
471 }
472
473
474
475
476
477 public static void copyInSession(Content src, String dest) throws RepositoryException {
478 final String destParentPath = StringUtils.defaultIfEmpty(StringUtils.substringBeforeLast(dest, "/"), "/");
479 final String destNodeName = StringUtils.substringAfterLast(dest, "/");
480 final Session session = src.getWorkspace().getSession();
481 try{
482 final File file = File.createTempFile("mgnl", null, Path.getTempDirectory());
483 final FileOutputStream outStream = new FileOutputStream(file);
484 session.exportSystemView(src.getHandle(), outStream, false, false);
485 outStream.flush();
486 IOUtils.closeQuietly(outStream);
487 FileInputStream inStream = new FileInputStream(file);
488 session.importXML(
489 destParentPath,
490 inStream,
491 ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
492 IOUtils.closeQuietly(inStream);
493 file.delete();
494 if(!StringUtils.equals(src.getName(), destNodeName)){
495 String currentPath;
496 if(destParentPath.equals("/")){
497 currentPath = "/" + src.getName();
498 }
499 else{
500 currentPath = destParentPath + "/" + src.getName();
501 }
502 session.move(currentPath, dest);
503 }
504 }
505 catch (IOException e) {
506 throw new RepositoryException("Can't copy node " + src + " to " + dest, e);
507 }
508 }
509
510
511
512
513 public static void moveInSession(Content src, String dest) throws RepositoryException {
514 src.getWorkspace().getSession().move(src.getHandle(), dest);
515 }
516
517 public static void rename(Content node, String newName) throws RepositoryException{
518 Content parent = node.getParent();
519 String placedBefore = null;
520 for (Iterator<Content> iter = parent.getChildren(node.getNodeTypeName()).iterator(); iter.hasNext();) {
521 Content child = iter.next();
522 if (child.getUUID().equals(node.getUUID())) {
523 if (iter.hasNext()) {
524 child = iter.next();
525 placedBefore = child.getName();
526 }
527 }
528 }
529
530 moveInSession(node, PathUtil.createPath(node.getParent().getHandle(), newName));
531
532
533 if (placedBefore != null) {
534 parent.orderBefore(newName, placedBefore);
535 parent.save();
536 }
537 }
538
539
540
541
542
543
544
545
546
547 public static void changeNodeType(Content node, ItemType newType, boolean replaceAll) throws RepositoryException{
548 if(node == null){
549 throw new IllegalArgumentException("Content can't be null");
550 }
551 if(newType == null){
552 throw new IllegalArgumentException("ItemType can't be null");
553 }
554 final String oldTypeName = node.getNodeTypeName();
555 final String newTypeName = newType.getSystemName();
556 if(newTypeName.equals(oldTypeName)){
557 log.info("Old node type and new one are the same {}. Nothing to change.", newTypeName);
558 return;
559 }
560 final Pattern nodeTypePattern = Pattern.compile("(<sv:property\\s+sv:name=\"jcr:primaryType\"\\s+sv:type=\"Name\"><sv:value>)("+oldTypeName+")(</sv:value>)");
561 final String replacement = "$1"+newTypeName+"$3";
562
563 log.debug("pattern is {}", nodeTypePattern.pattern());
564 log.debug("replacement string is {}", replacement);
565 log.debug("replaceAll? {}", replaceAll);
566
567 final String destParentPath = StringUtils.defaultIfEmpty(StringUtils.substringBeforeLast(node.getHandle(), "/"), "/");
568 final Session session = node.getWorkspace().getSession();
569 FileOutputStream outStream = null;
570 FileInputStream inStream = null;
571 File file = null;
572
573 try {
574 file = File.createTempFile("mgnl", null, Path.getTempDirectory());
575 outStream = new FileOutputStream(file);
576 session.exportSystemView(node.getHandle(), outStream, false, false);
577 outStream.flush();
578 final String fileContents = FileUtils.readFileToString(file);
579 log.debug("content string is {}", fileContents);
580 final Matcher matcher = nodeTypePattern.matcher(fileContents);
581 String replaced = null;
582
583 log.debug("starting find&replace...");
584 long start = System.currentTimeMillis();
585 if(matcher.find()) {
586 log.debug("{} will be replaced", node.getHandle());
587 if(replaceAll){
588 replaced = matcher.replaceAll(replacement);
589 } else {
590 replaced = matcher.replaceFirst(replacement);
591 }
592 log.debug("replaced string is {}", replaced);
593 } else {
594 log.debug("{} won't be replaced", node.getHandle());
595 return;
596 }
597 log.debug("find&replace operations took {}ms" + (System.currentTimeMillis() - start) / 1000);
598
599 FileUtils.writeStringToFile(file, replaced);
600 inStream = new FileInputStream(file);
601 session.importXML(
602 destParentPath,
603 inStream,
604 ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
605
606 } catch (IOException e) {
607 throw new RepositoryException("Can't replace node " + node.getHandle(), e);
608 } finally {
609 IOUtils.closeQuietly(outStream);
610 IOUtils.closeQuietly(inStream);
611 FileUtils.deleteQuietly(file);
612 }
613 }
614 }