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