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