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