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.jsp.taglib;
35
36 import info.magnolia.cms.core.Content.ContentFilter;
37 import info.magnolia.cms.i18n.I18nContentSupport;
38 import info.magnolia.cms.util.ContentUtil;
39 import info.magnolia.context.MgnlContext;
40 import info.magnolia.jcr.iterator.FilteringNodeIterator;
41 import info.magnolia.jcr.iterator.SameChildNodeTypeIterator;
42 import info.magnolia.jcr.predicate.NodeTypePredicate;
43 import info.magnolia.jcr.util.NodeTypes;
44 import info.magnolia.jcr.util.PropertyUtil;
45 import info.magnolia.objectfactory.Components;
46
47 import java.io.IOException;
48 import java.util.ArrayList;
49 import java.util.Iterator;
50 import java.util.List;
51
52 import javax.jcr.Node;
53 import javax.jcr.Property;
54 import javax.jcr.RepositoryException;
55 import javax.servlet.http.HttpServletRequest;
56 import javax.servlet.jsp.JspException;
57 import javax.servlet.jsp.JspWriter;
58 import javax.servlet.jsp.tagext.TagSupport;
59
60 import org.apache.commons.lang3.StringEscapeUtils;
61 import org.apache.commons.lang3.StringUtils;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64 import org.tldgen.annotations.BodyContent;
65 import org.tldgen.annotations.Tag;
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 @Tag(name = "simpleNavigation", bodyContent = BodyContent.EMPTY)
97 public class SimpleNavigationTag extends TagSupport {
98
99
100
101
102 private static final String CSS_LI_ACTIVE = "active";
103
104
105
106
107 private static final String CSS_LI_TRAIL = "trail";
108
109
110
111
112 private static final String CSS_LI_LEAF = "leaf";
113
114
115
116
117 private static final String CSS_LI_CLOSED = "closed";
118
119
120
121
122 private static final String CSS_LI_OPEN = "open";
123
124
125
126
127 private static final String CSS_LI_FIRST = "first";
128
129
130
131
132 private static final String CSS_LI_LAST = "last";
133
134
135
136
137 private static final String NODEDATA_NAVIGATIONTITLE = "navTitle";
138
139
140
141
142 public static final String NODEDATA_ACCESSKEY = "accessKey";
143
144
145
146
147 public static final String DEFAULT_OPENMENU_NODEDATA = "openMenu";
148
149
150
151
152 public static final String DEFAULT_HIDEINNAV_NODEDATA = "hideInNav";
153
154
155
156
157 public static final String DEFAULT_WRAPPERELEMENT_NODEDATA = "";
158
159
160
161
162 public static final String EXPAND_ALL = "all";
163
164
165
166
167 public static final String EXPAND_SHOW = "show";
168
169
170
171
172 public static final String EXPAND_NONE = "none";
173
174 private static Logger log = LoggerFactory.getLogger(SimpleNavigationTag.class);
175
176
177
178
179 private int startLevel;
180
181
182
183
184 private int endLevel;
185
186
187
188
189 private String hideInNav;
190
191
192
193
194 private String openMenu;
195
196
197
198
199 private String style;
200
201
202
203
204 public String wrapperElement;
205
206
207
208
209 private String expandAll = EXPAND_NONE;
210
211 private boolean relativeLevels = false;
212
213
214
215
216 private String classProperty;
217
218
219
220
221 private String nofollow;
222
223
224
225
226 private ContentFilter filter;
227
228 private String contentFilter = "";
229
230
231
232
233 private boolean markFirstAndLastElement = false;
234
235
236
237
238
239
240 public void setStartLevel(int startLevel) {
241 this.startLevel = startLevel;
242 }
243
244
245
246
247
248
249 public void setEndLevel(int endLevel) {
250 this.endLevel = endLevel;
251 }
252
253
254
255
256
257
258 public void setStyle(String style) {
259 this.style = style;
260 }
261
262
263
264
265
266
267
268 public void setHideInNav(String hideInNav) {
269 this.hideInNav = hideInNav;
270 }
271
272
273
274
275
276
277
278
279 public void setOpenMenu(String openMenu) {
280 this.openMenu = openMenu;
281 }
282
283
284
285
286
287
288
289
290 public void setNofollow(String nofollow) {
291 this.nofollow = nofollow;
292 }
293
294
295
296
297
298
299 public void setContentFilter(String contentFilter) {
300 this.contentFilter = contentFilter;
301 }
302
303
304
305
306
307
308
309 public void setExpandAll(String expandAll) {
310 if (expandAll.equalsIgnoreCase(EXPAND_SHOW)) {
311 this.expandAll = expandAll;
312 } else {
313 this.expandAll = EXPAND_ALL;
314 }
315 }
316
317
318
319
320
321
322
323 public void setRelativeLevels(boolean relativeLevels) {
324 this.relativeLevels = relativeLevels;
325 }
326
327
328
329
330
331
332 public void setClassProperty(String classProperty) {
333 this.classProperty = classProperty;
334 }
335
336
337
338
339
340
341
342 public void setWrapperElement(String wrapperElement) {
343 this.wrapperElement = wrapperElement;
344 }
345
346
347
348
349
350
351
352 public void setMarkFirstAndLastElement(boolean flag) {
353 markFirstAndLastElement = flag;
354 }
355
356 @Override
357 public int doEndTag() throws JspException {
358 Node activePage = getCurrentActivePageNode();
359 try {
360 while (!activePage.isNodeType(NodeTypes.Page.NAME) && activePage.getDepth() != 0) {
361 activePage = activePage.getParent();
362 }
363 } catch (RepositoryException e) {
364 log.error("Failed to obtain parent page for {}", activePage, e);
365 activePage = getCurrentActivePageNode();
366 }
367 JspWriter out = this.pageContext.getOut();
368
369 if (StringUtils.isNotEmpty(this.contentFilter)) {
370 try {
371 filter = (ContentFilter) this.pageContext.getAttribute(this.contentFilter);
372 } catch (ClassCastException e) {
373 log.error("contentFilter assigned was not a content filter", e);
374 }
375 } else {
376 filter = null;
377 }
378
379 if (startLevel > endLevel) {
380 endLevel = 0;
381 }
382
383 try {
384 final int activePageLevel = activePage.getDepth();
385
386
387 if (relativeLevels) {
388 this.startLevel += activePageLevel;
389 this.endLevel += activePageLevel;
390 }
391 if (this.startLevel <= activePageLevel) {
392 Node startContent = (Node) activePage.getAncestor(this.startLevel);
393 drawChildren(startContent, activePage, out);
394 }
395
396 } catch (RepositoryException e) {
397 log.error("RepositoryException caught while drawing navigation: {}", e.getMessage(), e);
398 return EVAL_PAGE;
399 } catch (IOException e) {
400
401 throw new JspException(e);
402 }
403
404 return EVAL_PAGE;
405 }
406
407 @Override
408 public void release() {
409 this.startLevel = 0;
410 this.endLevel = 0;
411 this.hideInNav = null;
412 this.openMenu = null;
413 this.style = null;
414 this.classProperty = null;
415 this.expandAll = EXPAND_NONE;
416 this.relativeLevels = false;
417 this.wrapperElement = "";
418 this.contentFilter = "";
419 this.filter = null;
420 this.nofollow = null;
421 this.markFirstAndLastElement = false;
422 super.release();
423 }
424
425
426
427
428
429
430
431
432
433
434 private void drawChildren(Node page, Node activePage, JspWriter out) throws IOException, RepositoryException {
435
436 Iterator<Node> children = new FilteringNodeIterator(page.getNodes(), new NodeTypePredicate(NodeTypes.Content.NAME));
437 I18nContentSupport i18nContentSupport = Components.getComponent(I18nContentSupport.class);
438
439 if (!children.hasNext()) {
440 return;
441 }
442
443 out.print("<ul class=\"level");
444 out.print(page.getDepth());
445 if (style != null && page.getDepth() == startLevel) {
446 out.print(" ");
447 out.print(style);
448 }
449 out.print("\">");
450
451 while (children.hasNext()) {
452 final Node child = children.next();
453
454
455 }
456
457 boolean isFirst = true;
458 Iterator<Node> visibleIt = new FilteringNodeIterator(page.getNodes(), new NodeTypePredicate(NodeTypes.Content.NAME));
459 while (visibleIt.hasNext()) {
460 Node child = visibleIt.next();
461
462
463 Property hideInNav = PropertyUtil.getPropertyOrNull(child, StringUtils.defaultString(this.hideInNav, DEFAULT_HIDEINNAV_NODEDATA));
464 if (expandAll.equalsIgnoreCase(EXPAND_NONE) || expandAll.equalsIgnoreCase(EXPAND_SHOW)) {
465 if (hideInNav != null && hideInNav.getBoolean()) {
466 continue;
467 }
468
469 if (filter != null) {
470 if (!filter.accept(ContentUtil.asContent(child))) {
471 continue;
472 }
473 }
474 } else {
475 if (hideInNav != null && hideInNav.getBoolean()) {
476 continue;
477 }
478 }
479
480 List<String> cssClasses = new ArrayList<String>(4);
481
482 Property propertyNavTitle = i18nContentSupport.getProperty(child, NODEDATA_NAVIGATIONTITLE);
483 String title = null;
484 if (propertyNavTitle != null) {
485 title = StringUtils.defaultString(propertyNavTitle.getString(), StringUtils.EMPTY);
486 }
487
488
489 if (StringUtils.isEmpty(title)) {
490 title = child.getProperty("title").getString();
491 }
492
493
494 if (StringUtils.isEmpty(title)) {
495 title = child.getName();
496 }
497
498 boolean showChildren = false;
499 boolean self = false;
500
501 if (!expandAll.equalsIgnoreCase(EXPAND_NONE)) {
502 showChildren = true;
503 }
504
505 if (activePage.getPath().equals(child.getPath())) {
506
507 showChildren = true;
508 self = true;
509 cssClasses.add(CSS_LI_ACTIVE);
510 } else if (!showChildren) {
511 showChildren = child.getDepth() < activePage.getDepth() && StringUtils.equals(activePage.getAncestor(child.getDepth()).getPath(), child.getPath());
512 }
513
514 if (!showChildren) {
515 showChildren = child
516 .getProperty(StringUtils.defaultString(this.openMenu, DEFAULT_OPENMENU_NODEDATA))
517 .getBoolean();
518 }
519
520 if (endLevel > 0) {
521 showChildren &= child.getDepth() < endLevel;
522 }
523
524 cssClasses.add(hasVisibleChildren(child) ? showChildren ? CSS_LI_OPEN : CSS_LI_CLOSED : CSS_LI_LEAF);
525
526 if (child.getDepth() < activePage.getDepth() && activePage.getAncestor(child.getDepth()).getPath().equals(child.getPath())) {
527 cssClasses.add(CSS_LI_TRAIL);
528 }
529
530 if (StringUtils.isNotEmpty(classProperty) && child.hasProperty(classProperty)) {
531 cssClasses.add(StringUtils.defaultString(child.getProperty(classProperty).getString(), StringUtils.EMPTY));
532 }
533
534 if (markFirstAndLastElement && isFirst) {
535 cssClasses.add(CSS_LI_FIRST);
536 isFirst = false;
537 }
538
539 if (markFirstAndLastElement && !visibleIt.hasNext()) {
540 cssClasses.add(CSS_LI_LAST);
541 }
542
543 StringBuffer css = new StringBuffer(cssClasses.size() * 10);
544 Iterator<String> iterator = cssClasses.iterator();
545 while (iterator.hasNext()) {
546 css.append(iterator.next());
547 if (iterator.hasNext()) {
548 css.append(" ");
549 }
550 }
551
552 out.print("<li class=\"");
553 out.print(css.toString());
554 out.print("\">");
555
556 if (self) {
557 out.println("<strong>");
558 }
559
560 String accesskey = null;
561 if (child.hasProperty(NODEDATA_ACCESSKEY)) {
562 accesskey = child.getProperty(NODEDATA_ACCESSKEY).getString();
563 }
564
565 out.print("<a href=\"");
566 out.print(((HttpServletRequest) this.pageContext.getRequest()).getContextPath());
567 out.print(i18nContentSupport.toI18NURI(child.getPath()));
568 out.print(".html\"");
569
570 if (StringUtils.isNotEmpty(accesskey)) {
571 out.print(" accesskey=\"");
572 out.print(accesskey);
573 out.print("\"");
574 }
575
576 if (nofollow != null && child.getProperty(this.nofollow).getBoolean()) {
577 out.print(" rel=\"nofollow\"");
578 }
579
580 out.print(">");
581
582 if (StringUtils.isNotEmpty(this.wrapperElement)) {
583 out.print("<" + this.wrapperElement + ">");
584 }
585
586 out.print(StringEscapeUtils.escapeHtml4(title));
587
588 if (StringUtils.isNotEmpty(this.wrapperElement)) {
589 out.print("</" + this.wrapperElement + ">");
590 }
591
592 out.print(" </a>");
593
594 if (self) {
595 out.println("</strong>");
596 }
597
598 if (showChildren) {
599 drawChildren(child, activePage, out);
600 }
601 out.print("</li>");
602 }
603
604 out.print("</ul>");
605 }
606
607
608
609
610
611
612
613 private boolean hasVisibleChildren(Node page) throws RepositoryException {
614 SameChildNodeTypeIterator children = new SameChildNodeTypeIterator(page);
615 if (children.hasNext() && expandAll.equalsIgnoreCase(EXPAND_ALL)) {
616 return true;
617 }
618 while (children.hasNext()) {
619 Property hideInNav = PropertyUtil.getPropertyOrNull(children.nextNode(), StringUtils.defaultString(this.hideInNav, DEFAULT_HIDEINNAV_NODEDATA));
620 if (hideInNav != null && !hideInNav.getBoolean()) {
621 return true;
622 }
623 }
624 return false;
625 }
626
627 protected Node getCurrentActivePageNode() {
628 Node currentActpage = MgnlContext.getAggregationState().getCurrentContentNode();
629 if (currentActpage == null) {
630 currentActpage = MgnlContext.getAggregationState().getMainContentNode();
631 }
632 return currentActpage;
633 }
634 }