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