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