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
501 public Node content(String path){
502 return content(RepositoryConstants.WEBSITE, path);
503 }
504
505
506
507
508
509
510 public Node content(String repository, String path){
511 return SessionUtil.getNode(repository, path);
512 }
513
514
515
516
517
518
519
520 public Node contentByIdentifier(String id){
521 return contentByIdentifier(RepositoryConstants.WEBSITE, id);
522 }
523
524
525
526
527
528
529
530 public Node contentByIdentifier(String repository, String id) {
531 return SessionUtil.getNodeByIdentifier(repository, id);
532 }
533
534
535
536
537
538 public ContentMap contentByPath(String path) {
539 return contentByPath(path, RepositoryConstants.WEBSITE);
540 }
541
542
543
544
545
546 public ContentMap contentByPath(String path, String workspace) {
547 return asContentMap(SessionUtil.getNode(workspace, path));
548 }
549
550
551
552
553
554 public ContentMap contentById(String id) {
555 return contentById(id, RepositoryConstants.WEBSITE);
556 }
557
558
559
560
561
562 public ContentMap contentById(String id, String workspace) {
563 return asContentMap(SessionUtil.getNodeByIdentifier(workspace, id));
564 }
565
566
567
568
569
570 public Node nodeByPath(String path) {
571 return nodeByPath(path, RepositoryConstants.WEBSITE);
572 }
573
574
575
576
577
578 public Node nodeByPath(String path, String workspace) {
579 return SessionUtil.getNode(workspace, path);
580 }
581
582
583
584
585
586 public Node nodeById(String id) {
587 return nodeById(id, RepositoryConstants.WEBSITE);
588 }
589
590
591
592
593
594 public Node nodeById(String id, String workspace) {
595 return SessionUtil.getNodeByIdentifier(workspace, id);
596 }
597
598 public List<ContentMap> asContentMapList(Collection<Node> nodeList) {
599 if (nodeList != null) {
600 List<ContentMap> contentMapList = new ArrayList<ContentMap>();
601 for (Node node : nodeList) {
602 contentMapList.add(asContentMap(node));
603 }
604 return contentMapList;
605 }
606 return null;
607 }
608
609 public List<Node> asNodeList(Collection<ContentMap> contentMapList) {
610 if (contentMapList != null) {
611 List<Node> nodeList = new ArrayList<Node>();
612 for (ContentMap node : contentMapList) {
613 nodeList.add(node.getJCRNode());
614 }
615 return nodeList;
616 }
617 return null;
618 }
619
620
621 protected List<Node> asNodeList(Iterable<Node> nodes) {
622 List<Node> childList = new ArrayList<Node>();
623 for (Node child : nodes) {
624 childList.add(child);
625 }
626 return childList;
627 }
628
629
630 protected List<ContentMap> asContentMapList(Iterable<Node> nodes) {
631 List<ContentMap> childList = new ArrayList<ContentMap>();
632 for (Node child : nodes) {
633 childList.add(new ContentMap(child));
634 }
635 return childList;
636 }
637
638
639
640
641
642
643
644 private boolean hasProtocol(String link) {
645 return link != null && link.contains("://");
646 }
647
648
649
650
651
652
653 private boolean isRoot(Node content) throws RepositoryException {
654 return content.getDepth() == 0;
655 }
656
657
658
659
660 public ContentMap decode(ContentMap content){
661 return asContentMap(decode(content.getJCRNode()));
662 }
663
664
665
666
667 public Node decode(Node content) {
668 return NodeUtil.deepUnwrap(content, HTMLEscapingNodeWrapper.class);
669 }
670
671
672
673
674 public ContentMap encode(ContentMap content) {
675 return content != null ? new ContentMap(new HTMLEscapingNodeWrapper(content.getJCRNode(), true)) : null;
676 }
677
678
679
680
681 public Node encode(Node content) {
682 return content != null ? new HTMLEscapingNodeWrapper(content, true) : null;
683 }
684
685 private Node wrapForInheritance(Node destination) throws RepositoryException {
686 ConfiguredInheritance inheritanceConfiguration = new ConfiguredInheritance();
687 inheritanceConfiguration.setEnabled(true);
688 return new DefaultInheritanceContentDecorator(destination, inheritanceConfiguration).wrapNode(destination);
689 }
690
691
692
693
694 public String metaData(Node content, String property){
695
696 Object returnValue;
697 try {
698 if (property.equals(NodeTypes.Created.CREATED)) {
699 returnValue = NodeTypes.Created.getCreated(content);
700 } else if (property.equals(NodeTypes.Created.CREATED_BY)) {
701 returnValue = NodeTypes.Created.getCreatedBy(content);
702 } else if (property.equals(NodeTypes.LastModified.LAST_MODIFIED)) {
703 returnValue = NodeTypes.LastModified.getLastModified(content);
704 } else if (property.equals(NodeTypes.LastModified.LAST_MODIFIED_BY)) {
705 returnValue = NodeTypes.LastModified.getLastModifiedBy(content);
706 } else if (property.equals(NodeTypes.Renderable.TEMPLATE)) {
707 returnValue = NodeTypes.Renderable.getTemplate(content);
708 } else if (property.equals(NodeTypes.Activatable.LAST_ACTIVATED)) {
709 returnValue = NodeTypes.Activatable.getLastActivated(content);
710 } else if (property.equals(NodeTypes.Activatable.LAST_ACTIVATED_BY)) {
711 returnValue = NodeTypes.Activatable.getLastActivatedBy(content);
712 } else if (property.equals(NodeTypes.Activatable.ACTIVATION_STATUS)) {
713 returnValue = NodeTypes.Activatable.getActivationStatus(content);
714 } else if (property.equals(NodeTypes.Deleted.DELETED)) {
715 returnValue = NodeTypes.Deleted.getDeleted(content);
716 } else if (property.equals(NodeTypes.Deleted.DELETED_BY)) {
717 returnValue = NodeTypes.Deleted.getDeletedBy(content);
718 } else if (property.equals(NodeTypes.Deleted.COMMENT)) {
719
720 returnValue = NodeTypes.Deleted.getComment(content);
721 } else {
722
723
724
725 returnValue = MetaDataUtil.getMetaData(content).getStringProperty(property);
726
727
728 log.warn("Deprecated constant [" + property+"] used to query for meta data property on node [" + NodeUtil.getPathIfPossible(content) + "]");
729 }
730 } catch (RepositoryException e) {
731 return "";
732 }
733
734 return returnValue instanceof Calendar ? ISO8601.format((Calendar) returnValue) : returnValue.toString();
735 }
736
737
738
739
740 public String metaData(ContentMap content, String property){
741 return metaData(content.getJCRNode(), property);
742 }
743
744
745
746
747
748
749 public Collection<Node> search(String workspace, String statement, String language, String returnItemType){
750 try {
751 return NodeUtil.getCollectionFromNodeIterator(QueryUtil.search(workspace, statement, language, returnItemType));
752 } catch (Exception e) {
753 log.error(e.getMessage(), e);
754 }
755 return null;
756 }
757
758
759
760
761
762
763
764 public Collection<Node> simpleSearch(String workspace, String statement, String returnItemType, String startPath){
765 if(StringUtils.isEmpty(statement)){
766 log.error("Cannot search with empty statement.");
767 return null;
768 }
769 String query = QueryUtil.buildQuery(statement, startPath);
770 try {
771 return NodeUtil.getCollectionFromNodeIterator(QueryUtil.search(workspace, query, "JCR-SQL2", returnItemType));
772 } catch (Exception e) {
773 log.error(e.getMessage(), e);
774 }
775 return null;
776 }
777 }