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.templating.functions;
35
36 import info.magnolia.cms.beans.config.ServerConfiguration;
37 import info.magnolia.cms.core.AggregationState;
38 import info.magnolia.cms.core.NodeData;
39 import info.magnolia.cms.i18n.I18nContentSupportFactory;
40 import info.magnolia.cms.util.ContentUtil;
41 import info.magnolia.cms.util.QueryUtil;
42 import info.magnolia.cms.util.SiblingsHelper;
43 import info.magnolia.jcr.inheritance.InheritanceNodeWrapper;
44 import info.magnolia.jcr.util.ContentMap;
45 import info.magnolia.jcr.util.MetaDataUtil;
46 import info.magnolia.jcr.util.NodeTypes;
47 import info.magnolia.jcr.util.NodeUtil;
48 import info.magnolia.jcr.util.PropertyUtil;
49 import info.magnolia.jcr.util.SessionUtil;
50 import info.magnolia.jcr.wrapper.HTMLEscapingNodeWrapper;
51 import info.magnolia.link.LinkUtil;
52 import info.magnolia.objectfactory.Components;
53 import info.magnolia.rendering.template.configured.ConfiguredInheritance;
54 import info.magnolia.repository.RepositoryConstants;
55 import info.magnolia.templating.inheritance.DefaultInheritanceContentDecorator;
56
57 import java.util.ArrayList;
58 import java.util.Calendar;
59 import java.util.Collection;
60 import java.util.List;
61
62 import javax.inject.Inject;
63 import javax.inject.Provider;
64 import javax.jcr.Node;
65 import javax.jcr.PathNotFoundException;
66 import javax.jcr.Property;
67 import javax.jcr.PropertyType;
68 import javax.jcr.RepositoryException;
69
70 import org.apache.commons.lang.StringUtils;
71 import org.apache.jackrabbit.util.ISO8601;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
74
75
76
77
78
79
80 public class TemplatingFunctions {
81
82 private static final Logger log = LoggerFactory.getLogger(TemplatingFunctions.class);
83
84 private final Provider<AggregationState> aggregationStateProvider;
85
86
87 @Inject
88 public TemplatingFunctions(Provider<AggregationState> aggregationStateProvider) {
89 this.aggregationStateProvider = aggregationStateProvider;
90 }
91
92
93 public Node asJCRNode(ContentMap contentMap) {
94 return contentMap == null ? null : contentMap.getJCRNode();
95 }
96
97 public ContentMap asContentMap(Node content) {
98 return content == null ? null : new ContentMap(content);
99 }
100
101 public List<Node> children(Node content) throws RepositoryException {
102 return content == null ? null : asNodeList(NodeUtil.getNodes(content, NodeUtil.EXCLUDE_META_DATA_FILTER));
103 }
104
105 public List<Node> children(Node content, String nodeTypeName) throws RepositoryException {
106 return content == null ? null : asNodeList(NodeUtil.getNodes(content, nodeTypeName));
107 }
108
109 public List<ContentMap> children(ContentMap content) throws RepositoryException {
110 return content == null ? null : asContentMapList(NodeUtil.getNodes(asJCRNode(content), NodeUtil.EXCLUDE_META_DATA_FILTER));
111 }
112
113 public List<ContentMap> children(ContentMap content, String nodeTypeName) throws RepositoryException {
114 return content == null ? null : asContentMapList(NodeUtil.getNodes(asJCRNode(content), nodeTypeName));
115 }
116
117 public ContentMap root(ContentMap contentMap) throws RepositoryException {
118 return contentMap == null ? null : asContentMap(this.root(contentMap.getJCRNode()));
119 }
120
121 public ContentMap root(ContentMap contentMap, String nodeTypeName) throws RepositoryException {
122 return contentMap == null ? null : asContentMap(this.root(contentMap.getJCRNode(), nodeTypeName));
123 }
124
125 public Node root(Node content) throws RepositoryException {
126 return this.root(content, null);
127 }
128
129 public Node root(Node content, String nodeTypeName) throws RepositoryException {
130 if (content == null) {
131 return null;
132 }
133 if (nodeTypeName == null) {
134 return (Node) content.getAncestor(0);
135 }
136 if (isRoot(content) && content.isNodeType(nodeTypeName)) {
137 return content;
138 }
139
140 Node parentNode = this.parent(content, nodeTypeName);
141 while (parent(parentNode, nodeTypeName) != null) {
142 parentNode = this.parent(parentNode, nodeTypeName);
143 }
144 return parentNode;
145 }
146
147 public ContentMap parent(ContentMap contentMap) throws RepositoryException {
148 return contentMap == null ? null : asContentMap(this.parent(contentMap.getJCRNode()));
149 }
150
151 public ContentMap parent(ContentMap contentMap, String nodeTypeName) throws RepositoryException {
152 return contentMap == null ? null : asContentMap(this.parent(contentMap.getJCRNode(), nodeTypeName));
153 }
154
155 public Node parent(Node content) throws RepositoryException {
156 return this.parent(content, null);
157 }
158
159 public Node parent(Node content, String nodeTypeName) throws RepositoryException {
160 if (content == null) {
161 return null;
162 }
163 if (isRoot(content)) {
164 return null;
165 }
166 if (nodeTypeName == null) {
167 return content.getParent();
168 }
169 Node parent = content.getParent();
170 while (!parent.isNodeType(nodeTypeName)) {
171 if (isRoot(parent)) {
172 return null;
173 }
174 parent = parent.getParent();
175 }
176 return parent;
177 }
178
179
180
181
182
183
184
185
186
187 public ContentMap page(ContentMap content) throws RepositoryException {
188 return content == null ? null : asContentMap(page(content.getJCRNode()));
189 }
190
191
192
193
194
195
196
197
198
199 public Node page(Node content) throws RepositoryException {
200 if (content == null) {
201 return null;
202 }
203 if (content.isNodeType(NodeTypes.Page.NAME)) {
204 return content;
205 }
206 return parent(content, NodeTypes.Page.NAME);
207 }
208
209 public List<ContentMap> ancestors(ContentMap contentMap) throws RepositoryException {
210 return ancestors(contentMap, null);
211 }
212
213 public List<ContentMap> ancestors(ContentMap contentMap, String nodeTypeName) throws RepositoryException {
214 List<Node> ancestorsAsNodes = this.ancestors(contentMap.getJCRNode(), nodeTypeName);
215 return asContentMapList(ancestorsAsNodes);
216 }
217
218 public List<Node> ancestors(Node content) throws RepositoryException {
219 return content == null ? null : this.ancestors(content, null);
220 }
221
222 public List<Node> ancestors(Node content, String nodeTypeName) throws RepositoryException {
223 if (content == null) {
224 return null;
225 }
226 List<Node> ancestors = new ArrayList<Node>();
227 int depth = content.getDepth();
228 for (int i = 1; i < depth; ++i) {
229 Node possibleAncestor = (Node) content.getAncestor(i);
230 if (nodeTypeName == null) {
231 ancestors.add(possibleAncestor);
232 } else {
233 if (possibleAncestor.isNodeType(nodeTypeName)) {
234 ancestors.add(possibleAncestor);
235 }
236 }
237 }
238 return ancestors;
239 }
240
241 public Node inherit(Node content) throws RepositoryException {
242 return inherit(content, null);
243 }
244
245 public Node inherit(Node content, String relPath) throws RepositoryException {
246 if (content == null) {
247 return null;
248 }
249 Node inheritedNode = wrapForInheritance(content);
250
251 if (StringUtils.isBlank(relPath)) {
252 return inheritedNode;
253 }
254
255 try {
256 Node subNode = inheritedNode.getNode(relPath);
257 return NodeUtil.unwrap(subNode);
258 } catch (PathNotFoundException e) {
259
260 }
261 return null;
262 }
263
264 public ContentMap inherit(ContentMap content) throws RepositoryException {
265 return inherit(content, null);
266 }
267
268 public ContentMap inherit(ContentMap content, String relPath) throws RepositoryException {
269 if (content == null) {
270 return null;
271 }
272 Node node = inherit(content.getJCRNode(), relPath);
273 return node == null ? null : new ContentMap(node);
274 }
275
276
277 public Property inheritProperty(Node content, String relPath) throws RepositoryException {
278 if (content == null) {
279 return null;
280 }
281 if (StringUtils.isBlank(relPath)) {
282 throw new IllegalArgumentException("relative path cannot be null or empty");
283 }
284 try {
285 Node inheritedNode = wrapForInheritance(content);
286 return inheritedNode.getProperty(relPath);
287
288 } catch (PathNotFoundException e) {
289
290 } catch (RepositoryException e) {
291
292 }
293
294 return null;
295 }
296
297 public Property inheritProperty(ContentMap content, String relPath) throws RepositoryException {
298 if (content == null) {
299 return null;
300 }
301 return inheritProperty(content.getJCRNode(), relPath);
302 }
303
304 public List<Node> inheritList(Node content, String relPath) throws RepositoryException {
305 if (content == null) {
306 return null;
307 }
308 if (StringUtils.isBlank(relPath)) {
309 throw new IllegalArgumentException("relative path cannot be null or empty");
310 }
311 Node inheritedNode = wrapForInheritance(content);
312 Node subNode = inheritedNode.getNode(relPath);
313 return children(subNode);
314 }
315
316 public List<ContentMap> inheritList(ContentMap content, String relPath) throws RepositoryException {
317 if (content == null) {
318 return null;
319 }
320 if (StringUtils.isBlank(relPath)) {
321 throw new IllegalArgumentException("relative path cannot be null or empty");
322 }
323 Node node = asJCRNode(content);
324 Node inheritedNode = wrapForInheritance(node);
325 Node subNode = inheritedNode.getNode(relPath);
326 return children(new ContentMap(subNode));
327 }
328
329 public boolean isInherited(Node content) {
330 if (content instanceof InheritanceNodeWrapper) {
331 return ((InheritanceNodeWrapper) content).isInherited();
332 }
333 return false;
334 }
335
336 public boolean isInherited(ContentMap content) {
337 return isInherited(asJCRNode(content));
338 }
339
340 public boolean isFromCurrentPage(Node content) {
341 return !isInherited(content);
342 }
343
344 public boolean isFromCurrentPage(ContentMap content) {
345 return isFromCurrentPage(asJCRNode(content));
346 }
347
348
349
350
351 public String link(String workspace, String nodeIdentifier) {
352 try {
353 return LinkUtil.createLink(workspace, nodeIdentifier);
354 } catch (RepositoryException e) {
355 return null;
356 }
357 }
358
359
360
361
362
363
364 @Deprecated
365 public String link(Property property) {
366 try {
367 Node parentNode = null;
368 String propertyName = null;
369 if (property.getType() == PropertyType.BINARY) {
370 parentNode = property.getParent().getParent();
371 propertyName = property.getParent().getName();
372 } else {
373 parentNode = property.getParent();
374 propertyName = property.getName();
375 }
376 NodeData equivNodeData = ContentUtil.asContent(parentNode).getNodeData(propertyName);
377 return LinkUtil.createLink(equivNodeData);
378 } catch (Exception e) {
379 return null;
380 }
381 }
382
383 public String link(Node content) {
384 return content == null ? null : LinkUtil.createLink(content);
385 }
386
387 public String link(ContentMap contentMap) throws RepositoryException {
388 return contentMap == null ? null : this.link(asJCRNode(contentMap));
389 }
390
391
392
393
394
395 public String language(){
396 return I18nContentSupportFactory.getI18nSupport().getLocale().toString();
397 }
398
399
400
401
402
403
404
405
406 public String externalLink(Node content, String linkPropertyName) {
407 String externalLink = PropertyUtil.getString(content, linkPropertyName);
408 if (StringUtils.isBlank(externalLink)) {
409 return StringUtils.EMPTY;
410 }
411 if (!hasProtocol(externalLink)) {
412 externalLink = "http://" + externalLink;
413 }
414 return externalLink;
415 }
416
417
418
419
420
421
422
423
424
425 public String externalLink(ContentMap content, String linkPropertyName) {
426 return externalLink(asJCRNode(content), linkPropertyName);
427 }
428
429
430
431
432
433
434
435
436
437
438 public String externalLinkTitle(Node content, String linkPropertyName, String linkTitlePropertyName) {
439 String linkTitle = PropertyUtil.getString(content, linkTitlePropertyName);
440 if (StringUtils.isNotEmpty(linkTitle)) {
441 return linkTitle;
442 }
443 return externalLink(content, linkPropertyName);
444 }
445
446
447
448
449
450
451
452
453
454
455 public String externalLinkTitle(ContentMap content, String linkPropertyName, String linkTitlePropertyName) {
456 return externalLinkTitle(asJCRNode(content), linkPropertyName, linkTitlePropertyName);
457 }
458
459 public boolean isEditMode() {
460
461 return isAuthorInstance() && !isPreviewMode();
462 }
463
464 public boolean isPreviewMode() {
465 return this.aggregationStateProvider.get().isPreviewMode();
466 }
467
468 public boolean isAuthorInstance() {
469 return Components.getComponent(ServerConfiguration.class).isAdmin();
470 }
471
472 public boolean isPublicInstance() {
473 return !isAuthorInstance();
474 }
475
476
477
478
479
480 public String createHtmlAttribute(String name, String value) {
481 value = StringUtils.trim(value);
482 if (StringUtils.isNotEmpty(value)) {
483 return new StringBuffer().append(name).append("=\"").append(value).append("\"").toString();
484 }
485 return StringUtils.EMPTY;
486 }
487
488
489
490
491 public SiblingsHelper siblings(Node node) throws RepositoryException {
492 return SiblingsHelper.of(ContentUtil.asContent(node));
493 }
494
495 public SiblingsHelper siblings(ContentMap node) throws RepositoryException {
496 return siblings(asJCRNode(node));
497 }
498
499
500
501
502
503
504
505 public Node content(String path){
506 return content(RepositoryConstants.WEBSITE, path);
507 }
508
509
510
511
512
513
514 public Node content(String repository, String path){
515 return SessionUtil.getNode(repository, path);
516 }
517
518
519
520
521
522
523
524 public Node contentByIdentifier(String id){
525 return contentByIdentifier(RepositoryConstants.WEBSITE, id);
526 }
527
528
529
530
531
532
533
534 public Node contentByIdentifier(String repository, String id) {
535 return SessionUtil.getNodeByIdentifier(repository, id);
536 }
537
538
539
540
541
542 public ContentMap contentByPath(String path) {
543 return contentByPath(path, RepositoryConstants.WEBSITE);
544 }
545
546
547
548
549
550 public ContentMap contentByPath(String path, String workspace) {
551 return asContentMap(SessionUtil.getNode(workspace, path));
552 }
553
554
555
556
557
558 public ContentMap contentById(String id) {
559 return contentById(id, RepositoryConstants.WEBSITE);
560 }
561
562
563
564
565
566 public ContentMap contentById(String id, String workspace) {
567 return asContentMap(SessionUtil.getNodeByIdentifier(workspace, id));
568 }
569
570
571
572
573
574 public Node nodeByPath(String path) {
575 return nodeByPath(path, RepositoryConstants.WEBSITE);
576 }
577
578
579
580
581
582 public Node nodeByPath(String path, String workspace) {
583 return SessionUtil.getNode(workspace, path);
584 }
585
586
587
588
589
590 public Node nodeById(String id) {
591 return nodeById(id, RepositoryConstants.WEBSITE);
592 }
593
594
595
596
597
598 public Node nodeById(String id, String workspace) {
599 return SessionUtil.getNodeByIdentifier(workspace, id);
600 }
601
602 public List<ContentMap> asContentMapList(Collection<Node> nodeList) {
603 if (nodeList != null) {
604 List<ContentMap> contentMapList = new ArrayList<ContentMap>();
605 for (Node node : nodeList) {
606 contentMapList.add(asContentMap(node));
607 }
608 return contentMapList;
609 }
610 return null;
611 }
612
613 public List<Node> asNodeList(Collection<ContentMap> contentMapList) {
614 if (contentMapList != null) {
615 List<Node> nodeList = new ArrayList<Node>();
616 for (ContentMap node : contentMapList) {
617 nodeList.add(node.getJCRNode());
618 }
619 return nodeList;
620 }
621 return null;
622 }
623
624
625 protected List<Node> asNodeList(Iterable<Node> nodes) {
626 List<Node> childList = new ArrayList<Node>();
627 for (Node child : nodes) {
628 childList.add(child);
629 }
630 return childList;
631 }
632
633
634 protected List<ContentMap> asContentMapList(Iterable<Node> nodes) {
635 List<ContentMap> childList = new ArrayList<ContentMap>();
636 for (Node child : nodes) {
637 childList.add(new ContentMap(child));
638 }
639 return childList;
640 }
641
642
643
644
645
646
647
648 private boolean hasProtocol(String link) {
649 return link != null && link.contains("://");
650 }
651
652
653
654
655
656
657
658 private boolean isRoot(Node content) throws RepositoryException {
659 return content.getDepth() == 0;
660 }
661
662
663
664
665 public ContentMap decode(ContentMap content){
666 return asContentMap(decode(content.getJCRNode()));
667 }
668
669
670
671
672 public Node decode(Node content) {
673 return NodeUtil.deepUnwrap(content, HTMLEscapingNodeWrapper.class);
674 }
675
676
677
678
679 public ContentMap encode(ContentMap content) {
680 return content != null ? new ContentMap(new HTMLEscapingNodeWrapper(content.getJCRNode(), true)) : null;
681 }
682
683
684
685
686 public Node encode(Node content) {
687 return content != null ? new HTMLEscapingNodeWrapper(content, true) : null;
688 }
689
690 private Node wrapForInheritance(Node destination) throws RepositoryException {
691 ConfiguredInheritance inheritanceConfiguration = new ConfiguredInheritance();
692 inheritanceConfiguration.setEnabled(true);
693 return new DefaultInheritanceContentDecorator(destination, inheritanceConfiguration).wrapNode(destination);
694 }
695
696
697
698
699 public String metaData(Node content, String property){
700
701 Object returnValue;
702 try {
703 if (property.equals(NodeTypes.Created.CREATED)) {
704 returnValue = NodeTypes.Created.getCreated(content);
705 } else if (property.equals(NodeTypes.Created.CREATED_BY)) {
706 returnValue = NodeTypes.Created.getCreatedBy(content);
707 } else if (property.equals(NodeTypes.LastModified.LAST_MODIFIED)) {
708 returnValue = NodeTypes.LastModified.getLastModified(content);
709 } else if (property.equals(NodeTypes.LastModified.LAST_MODIFIED_BY)) {
710 returnValue = NodeTypes.LastModified.getLastModifiedBy(content);
711 } else if (property.equals(NodeTypes.Renderable.TEMPLATE)) {
712 returnValue = NodeTypes.Renderable.getTemplate(content);
713 } else if (property.equals(NodeTypes.Activatable.LAST_ACTIVATED)) {
714 returnValue = NodeTypes.Activatable.getLastActivated(content);
715 } else if (property.equals(NodeTypes.Activatable.LAST_ACTIVATED_BY)) {
716 returnValue = NodeTypes.Activatable.getLastActivatedBy(content);
717 } else if (property.equals(NodeTypes.Activatable.ACTIVATION_STATUS)) {
718 returnValue = NodeTypes.Activatable.getActivationStatus(content);
719 } else if (property.equals(NodeTypes.Deleted.DELETED)) {
720 returnValue = NodeTypes.Deleted.getDeleted(content);
721 } else if (property.equals(NodeTypes.Deleted.DELETED_BY)) {
722 returnValue = NodeTypes.Deleted.getDeletedBy(content);
723 } else if (property.equals(NodeTypes.Deleted.COMMENT)) {
724
725 returnValue = NodeTypes.Deleted.getComment(content);
726 } else {
727
728
729
730 returnValue = MetaDataUtil.getMetaData(content).getStringProperty(property);
731
732
733 log.warn("Deprecated constant [" + property+"] used to query for meta data property on node [" + NodeUtil.getPathIfPossible(content) + "]");
734 }
735 } catch (RepositoryException e) {
736 return "";
737 }
738
739 return returnValue instanceof Calendar ? ISO8601.format((Calendar) returnValue) : returnValue.toString();
740 }
741
742
743
744
745 public String metaData(ContentMap content, String property){
746 return metaData(content.getJCRNode(), property);
747 }
748
749
750
751
752
753
754
755
756
757 public Collection<Node> search(String workspace, String statement, String language, String returnItemType){
758 try {
759 return NodeUtil.getCollectionFromNodeIterator(QueryUtil.search(workspace, statement, language, returnItemType));
760 } catch (Exception e) {
761 log.error(e.getMessage(), e);
762 }
763 return null;
764 }
765
766
767
768
769
770
771
772
773
774 public Collection<Node> simpleSearch(String workspace, String statement, String returnItemType, String startPath){
775 if(StringUtils.isEmpty(statement)){
776 log.error("Cannot search with empty statement.");
777 return null;
778 }
779 String query = QueryUtil.buildQuery(statement, startPath);
780 try {
781 return NodeUtil.getCollectionFromNodeIterator(QueryUtil.search(workspace, query, "JCR-SQL2", returnItemType));
782 } catch (Exception e) {
783 log.error(e.getMessage(), e);
784 }
785 return null;
786 }
787 }