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