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.core.version;
35
36 import info.magnolia.cms.core.AbstractContent;
37 import info.magnolia.cms.core.Content;
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.NodeData;
42 import info.magnolia.cms.security.AccessDeniedException;
43 import info.magnolia.cms.security.AccessManager;
44 import info.magnolia.cms.security.Permission;
45 import info.magnolia.cms.util.ContentWrapper;
46 import info.magnolia.cms.util.NodeDataWrapper;
47 import info.magnolia.cms.util.Rule;
48
49 import java.util.ArrayList;
50 import java.util.Calendar;
51 import java.util.Collection;
52 import java.util.Comparator;
53 import java.util.List;
54
55 import javax.jcr.Node;
56 import javax.jcr.PathNotFoundException;
57 import javax.jcr.PropertyType;
58 import javax.jcr.RepositoryException;
59 import javax.jcr.Value;
60 import javax.jcr.Workspace;
61 import javax.jcr.lock.Lock;
62 import javax.jcr.lock.LockException;
63 import javax.jcr.nodetype.NodeType;
64 import javax.jcr.nodetype.NodeTypeManager;
65 import javax.jcr.version.Version;
66 import javax.jcr.version.VersionHistory;
67 import javax.jcr.version.VersionIterator;
68
69 import org.apache.commons.lang.StringUtils;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72
73
74
75
76
77 public class ContentVersion extends DefaultContent {
78
79
80
81
82 private final class ContentVersionChildWrapper extends ContentWrapper {
83 private final Content parent;
84
85 private ContentVersionChildWrapper(Content wrappedContent, Content parent) {
86 super(wrappedContent);
87 this.parent = parent;
88 }
89
90 @Override
91 public Content getParent() throws RepositoryException {
92 return parent;
93 }
94
95
96
97
98 @Override
99 public String getHandle() {
100 try {
101 return getParent().getHandle() + "/" + getName();
102 }
103 catch (RepositoryException e) {
104 throw new RuntimeException("Can't create handle for versioned node.", e);
105 }
106 }
107
108
109
110
111 @Override
112 public NodeData newNodeDataInstance(String name, int type, boolean createIfNotExisting) throws AccessDeniedException, RepositoryException {
113 return new NodeDataWrapper(super.newNodeDataInstance(name, type, createIfNotExisting)) {
114 @Override
115 public String getHandle() {
116 return ContentVersionChildWrapper.this.getHandle() + "/" + getName();
117 }
118 };
119 }
120
121 @Override
122 protected Content wrap(Content node) {
123 return new ContentVersionChildWrapper(node, this);
124 }
125 }
126
127 private static Logger log = LoggerFactory.getLogger(ContentVersion.class);
128
129
130
131
132 public static final String VERSION_USER = "versionUser";
133
134
135
136
137 public static final String NAME = "name";
138
139
140
141
142 private final Version state;
143
144
145
146
147 private final Content base;
148
149
150
151
152 private Rule rule;
153
154 private final Version versionedNode;
155
156 public ContentVersion(Version thisVersion, Content base) throws RepositoryException {
157 if (thisVersion == null) {
158 throw new RepositoryException("Failed to get ContentVersion, version does not exist");
159 }
160 this.versionedNode = thisVersion;
161 if (thisVersion instanceof VersionedNode) {
162 this.state = ((VersionedNode) thisVersion).unwrap();
163 } else {
164 this.state = thisVersion;
165 }
166 this.base = base;
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191 this.init();
192 }
193
194
195
196
197 private void init() throws RepositoryException {
198 this.setNode(this.state.getNode(ItemType.JCR_FROZENNODE));
199 try {
200 if (!StringUtils.equalsIgnoreCase(this.state.getName(), VersionManager.ROOT_VERSION)) {
201 this.rule = VersionManager.getInstance().getUsedFilter(this.getJCRNode());
202 }
203 }
204 catch (Exception e) {
205 log.error(e.getMessage(), e);
206 }
207 if (this.rule == null) {
208 log.info("failed to get filter used for creating this version, use open filter");
209 this.rule = new Rule();
210 }
211 }
212
213
214
215
216
217
218 public Calendar getCreated() throws RepositoryException {
219 return this.state.getCreated();
220 }
221
222
223
224
225
226
227 public String getVersionLabel() throws RepositoryException {
228 return this.state.getName();
229 }
230
231
232
233
234
235
236 public VersionHistory getContainingHistory() throws RepositoryException {
237 return this.state.getContainingHistory();
238 }
239
240
241
242
243 @Override
244 public String getName() {
245 try {
246 return VersionManager.getInstance().getSystemNode(this.getJCRNode()).getProperty(NAME).getString();
247 }
248 catch (RepositoryException re) {
249 log.error("Failed to retrieve name from version system node", re);
250 return "";
251 }
252 }
253
254
255
256
257 public String getUserName() {
258 try {
259 return VersionManager.getInstance().getSystemNode(this.getJCRNode()).getProperty(VERSION_USER).getString();
260 }
261 catch (RepositoryException re) {
262 log.error("Failed to retrieve user from version system node", re);
263 return "";
264 }
265 }
266
267
268
269
270 @Override
271 public String getHandle() {
272 return this.base.getHandle();
273 }
274
275
276
277
278
279 @Override
280 public Content getContent(String name) throws PathNotFoundException, RepositoryException, AccessDeniedException {
281
282 if(super.hasContent(name)){
283 return new ContentVersionChildWrapper(super.getContent(name), this);
284 }
285 Content content = base.getContent(name);
286
287 if(!rule.isAllowed(content.getJCRNode())){
288 return content;
289 }
290 throw new PathNotFoundException(base.getHandle() + "/" + name);
291 }
292
293
294
295
296 @Override
297 public boolean hasContent(String name) throws RepositoryException {
298 if(super.hasContent(name)){
299 return true;
300 }
301 else if(base.hasContent(name)){
302 Content content = base.getContent(name);
303
304 if(!rule.isAllowed(content.getJCRNode())){
305 return true;
306 }
307 }
308 return false;
309 }
310
311
312
313
314 @Override
315 public Content createContent(String name) throws AccessDeniedException {
316 throw new AccessDeniedException("Not allowed to write on version preview");
317 }
318
319
320
321
322 @Override
323 public Content createContent(String name, String contentType) throws AccessDeniedException {
324 throw new AccessDeniedException("Not allowed to write on version preview");
325 }
326
327
328
329
330 @Override
331 public Content createContent(String name, ItemType contentType) throws AccessDeniedException {
332 throw new AccessDeniedException("Not allowed to write on version preview");
333 }
334
335
336
337
338 @Override
339 public NodeData createNodeData(String name) throws AccessDeniedException {
340 throw new AccessDeniedException("Not allowed to write on version preview");
341 }
342
343
344
345
346 public NodeData createNodeData(String name, Value value, int type) throws AccessDeniedException {
347 throw new AccessDeniedException("Not allowed to write on version preview");
348 }
349
350
351
352
353 @Override
354 public NodeData createNodeData(String name, Value value) throws AccessDeniedException {
355 throw new AccessDeniedException("Not allowed to write on version preview");
356 }
357
358
359
360
361 @Override
362 public NodeData createNodeData(String name, int type) throws AccessDeniedException {
363 throw new AccessDeniedException("Not allowed to write on version preview");
364 }
365
366
367
368
369 @Override
370 public void deleteNodeData(String name) throws RepositoryException {
371 throw new AccessDeniedException("Not allowed to write on version preview");
372 }
373
374
375
376
377 @Override
378 public void updateMetaData() throws AccessDeniedException {
379 throw new AccessDeniedException("Not allowed to write on version preview");
380 }
381
382
383
384
385
386 @Override
387 public Collection<Content> getChildren(ContentFilter filter, String namePattern, Comparator<Content> orderCriteria) {
388 ArrayList<Content> result = new ArrayList<Content>();
389 result.addAll(wrap(super.getChildren(filter, namePattern, orderCriteria)));
390
391 Collection<Content> transientChildren = ((AbstractContent)this.base).getChildren(filter, namePattern, orderCriteria);
392 for (Content transientChild : transientChildren) {
393 try {
394 if(!rule.isAllowed(transientChild.getJCRNode())){
395 result.add(transientChild);
396 }
397 }
398 catch (RepositoryException e) {
399 throw new RuntimeException("Can't determine node type of " + transientChild, e);
400 }
401 }
402
403 return result;
404 }
405
406 private Collection<Content> wrap(Collection<Content> children) {
407 List<Content> transformed = new ArrayList<Content>();
408 for (Content child : children) {
409 transformed.add(new ContentVersionChildWrapper(child, this));
410 }
411 return transformed;
412 }
413
414
415
416
417 @Override
418 public boolean hasChildren() {
419 return (this.getChildren().size() > 0);
420 }
421
422
423
424
425
426 @Override
427 public boolean hasChildren(String contentType) {
428 return (this.getChildren(contentType).size() > 0);
429 }
430
431
432
433
434 @Override
435 public Content getParent() throws PathNotFoundException, RepositoryException, AccessDeniedException {
436 return this.base.getParent();
437 }
438
439 @Override
440 public Content getAncestor(int level) throws PathNotFoundException, RepositoryException, AccessDeniedException {
441 return this.base.getAncestor(level);
442 }
443
444
445
446
447
448
449 @Override
450 public Collection<Content> getAncestors() throws PathNotFoundException, RepositoryException {
451 return this.base.getAncestors();
452 }
453
454
455
456
457
458
459
460 @Override
461 public int getLevel() throws PathNotFoundException, RepositoryException {
462 return this.base.getLevel();
463 }
464
465
466
467
468 @Override
469 public void orderBefore(String srcName, String beforeName) throws RepositoryException {
470 throw new AccessDeniedException("Not allowed to write on version preview");
471 }
472
473
474
475
476
477
478
479
480
481 @Override
482 public int getIndex() throws RepositoryException {
483 return this.base.getIndex();
484 }
485
486
487
488
489
490 @Override
491 public NodeType getNodeType() throws RepositoryException {
492 log.warn("This is a Version node, it will always return NT_FROZEN as node type.");
493 log.warn("Use getNodeTypeName to retrieve base node primary type");
494 return super.getNodeType();
495 }
496
497
498
499
500 @Override
501 public void restore(String versionName, boolean removeExisting) throws RepositoryException {
502 throw new AccessDeniedException("Not allowed to write on version preview");
503 }
504
505
506
507
508 @Override
509 public void restore(Version version, boolean removeExisting) throws RepositoryException {
510 throw new AccessDeniedException("Not allowed to write on version preview");
511 }
512
513
514
515
516 @Override
517 public void restore(Version version, String relPath, boolean removeExisting) throws RepositoryException {
518 throw new AccessDeniedException("Not allowed to write on version preview");
519 }
520
521
522
523
524 @Override
525 public void restoreByLabel(String versionLabel, boolean removeExisting) throws RepositoryException {
526 throw new AccessDeniedException("Not allowed to write on version preview");
527 }
528
529
530
531
532 @Override
533 public Version addVersion() throws RepositoryException {
534 throw new AccessDeniedException("Not allowed to add version on version preview");
535 }
536
537
538
539
540 @Override
541 public Version addVersion(Rule rule) throws RepositoryException {
542 throw new AccessDeniedException("Not allowed to add version on version preview");
543 }
544
545
546
547
548 @Override
549 public boolean isModified() {
550 log.error("Not valid for version");
551 return false;
552 }
553
554
555
556
557 @Override
558 public VersionHistory getVersionHistory() throws RepositoryException {
559 throw new AccessDeniedException("Not allowed to read VersionHistory of Version");
560 }
561
562
563
564
565 @Override
566 public VersionIterator getAllVersions() throws RepositoryException {
567 throw new AccessDeniedException("Not allowed to get VersionIterator of Version");
568 }
569
570
571
572
573 @Override
574 public ContentVersion getBaseVersion() throws RepositoryException {
575 throw new AccessDeniedException("Not allowed to get base version of Version");
576 }
577
578
579
580
581 @Override
582 public ContentVersion getVersionedContent(Version version) throws RepositoryException {
583 throw new AccessDeniedException("Not allowed to get preview of Version itself");
584 }
585
586
587
588
589 @Override
590 public ContentVersion getVersionedContent(String versionName) throws RepositoryException {
591 throw new AccessDeniedException("Not allowed to get preview of Version itself");
592 }
593
594
595
596
597 @Override
598 public void save() throws RepositoryException {
599 throw new AccessDeniedException("Not allowed to write on version preview");
600 }
601
602
603
604
605
606
607 @Override
608 public boolean isGranted(long permissions) {
609 return (permissions & Permission.READ) == permissions;
610 }
611
612
613
614
615 @Override
616 public void delete() throws RepositoryException {
617 throw new AccessDeniedException("Not allowed to write on version preview");
618 }
619
620
621
622
623 @Override
624 public void delete(String path) throws RepositoryException {
625 throw new AccessDeniedException("Not allowed to write on version preview");
626 }
627
628
629
630
631
632 @Override
633 public String getUUID() {
634 return this.base.getUUID();
635 }
636
637
638
639
640 @Override
641 public void addMixin(String type) throws RepositoryException {
642 throw new AccessDeniedException("Not allowed to write on version preview");
643 }
644
645
646
647
648 @Override
649 public void removeMixin(String type) throws RepositoryException {
650 throw new AccessDeniedException("Not allowed to write on version preview");
651 }
652
653
654
655
656 @Override
657 public Lock lock(boolean isDeep, boolean isSessionScoped) throws LockException, RepositoryException {
658 throw new AccessDeniedException("Lock not supported on version preview");
659 }
660
661
662
663
664 @Override
665 public Lock lock(boolean isDeep, boolean isSessionScoped, long yieldFor) throws LockException, RepositoryException {
666 throw new AccessDeniedException("Lock not supported on version preview");
667 }
668
669
670
671
672 @Override
673 public Lock getLock() throws LockException, RepositoryException {
674 throw new AccessDeniedException("Lock not supported on version preview");
675 }
676
677
678
679
680 @Override
681 public void unlock() throws LockException, RepositoryException {
682 throw new AccessDeniedException("Lock not supported on version preview");
683 }
684
685
686
687
688 @Override
689 public boolean holdsLock() throws RepositoryException {
690 throw new AccessDeniedException("Lock not supported on version preview");
691 }
692
693
694
695
696 @Override
697 public boolean isLocked() throws RepositoryException {
698 throw new AccessDeniedException("Lock not supported on version preview");
699 }
700
701
702
703
704 @Override
705 public HierarchyManager getHierarchyManager() {
706 return this.base.getHierarchyManager();
707 }
708
709
710
711
712
713
714 @Deprecated
715 @Override
716 public AccessManager getAccessManager() {
717 return this.base.getAccessManager();
718 }
719
720 @Override
721 public Workspace getWorkspace() throws RepositoryException {
722 return this.base.getWorkspace();
723 }
724
725 @Override
726 public boolean hasNodeData(String name) throws RepositoryException {
727 if (this.node.hasProperty(name)) {
728 return true;
729 }
730 if (this.node.hasNode(name) && this.node.getNode(name).getProperty("jcr:frozenPrimaryType").getValue().getString().equals(ItemType.NT_RESOURCE)) {
731 return true;
732 }
733 return false;
734 }
735
736 @Override
737 protected int determineNodeDataType(String name) {
738
739 try {
740 if (this.node.hasProperty(name)) {
741 return this.node.getProperty(name).getType();
742 }
743 if (this.node.hasNode(name) && this.node.getNode(name).getProperty("jcr:frozenPrimaryType").getValue().getString().equals(ItemType.NT_RESOURCE)) {
744 return PropertyType.BINARY;
745 }
746 }
747 catch (RepositoryException e) {
748 throw new IllegalStateException("Can't determine property type of [" + getHandle() + "/" + name + "]", e);
749 }
750 return PropertyType.UNDEFINED;
751 }
752
753 @Override
754 public NodeType[] getMixinNodeTypes() throws RepositoryException {
755 Value[] vals = this.node.getProperty("jcr:frozenMixinTypes").getValues();
756 NodeTypeManager typeMan = getJCRNode().getSession().getWorkspace().getNodeTypeManager();
757 NodeType[] types = new NodeType[vals.length];
758 int i = 0;
759 for (Value val : vals) {
760 types[i++] = typeMan.getNodeType(val.getString());
761 }
762 return types;
763 }
764
765
766
767
768
769
770
771
772 public Version[] getPredecessors() throws RepositoryException {
773
774 return this.state.getPredecessors();
775 }
776
777 @Override
778 public Node getJCRNode() {
779
780 try {
781 if (versionedNode.hasNode("jcr:frozenNode")) {
782 return versionedNode.getFrozenNode();
783 }
784 } catch (RepositoryException e) {
785 log.error("Failed to retrieve frozen node from version " + versionedNode, e);
786 }
787
788 return versionedNode;
789 }
790 }