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