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