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 @Tag(name = "simpleNavigation", bodyContent = BodyContent.EMPTY)
96 public class SimpleNavigationTag extends TagSupport {
97
98
99
100
101 private static final String CSS_LI_ACTIVE = "active";
102
103
104
105
106 private static final String CSS_LI_TRAIL = "trail";
107
108
109
110
111 private static final String CSS_LI_LEAF = "leaf";
112
113
114
115
116 private static final String CSS_LI_CLOSED = "closed";
117
118
119
120
121 private static final String CSS_LI_OPEN = "open";
122
123
124
125
126 private static final String CSS_LI_FIRST = "first";
127
128
129
130
131 private static final String CSS_LI_LAST = "last";
132
133
134
135
136 private static final String NODEDATA_NAVIGATIONTITLE = "navTitle";
137
138
139
140
141 public static final String NODEDATA_ACCESSKEY = "accessKey";
142
143
144
145
146 public static final String DEFAULT_OPENMENU_NODEDATA = "openMenu";
147
148
149
150
151 public static final String DEFAULT_HIDEINNAV_NODEDATA = "hideInNav";
152
153
154
155
156 public static final String DEFAULT_WRAPPERELEMENT_NODEDATA = "";
157
158
159
160
161 public static final String EXPAND_ALL = "all";
162
163
164
165
166 public static final String EXPAND_SHOW = "show";
167
168
169
170
171 public static final String EXPAND_NONE = "none";
172
173 private static Logger log = LoggerFactory.getLogger(SimpleNavigationTag.class);
174
175
176
177
178 private int startLevel;
179
180
181
182
183 private int endLevel;
184
185
186
187
188 private String hideInNav;
189
190
191
192
193 private String openMenu;
194
195
196
197
198 private String style;
199
200
201
202
203 public String wrapperElement;
204
205
206
207
208 private String expandAll = EXPAND_NONE;
209
210 private boolean relativeLevels = false;
211
212
213
214
215 private String classProperty;
216
217
218
219
220 private String nofollow;
221
222
223
224
225 private ContentFilter filter;
226
227 private String contentFilter = "";
228
229
230
231
232 private boolean markFirstAndLastElement = false;
233
234
235
236
237
238
239 public void setStartLevel(int startLevel) {
240 this.startLevel = startLevel;
241 }
242
243
244
245
246
247
248 public void setEndLevel(int endLevel) {
249 this.endLevel = endLevel;
250 }
251
252
253
254
255
256
257 public void setStyle(String style) {
258 this.style = style;
259 }
260
261
262
263
264
265
266
267 public void setHideInNav(String hideInNav) {
268 this.hideInNav = hideInNav;
269 }
270
271
272
273
274
275
276
277
278 public void setOpenMenu(String openMenu) {
279 this.openMenu = openMenu;
280 }
281
282
283
284
285
286
287
288
289 public void setNofollow(String nofollow) {
290 this.nofollow = nofollow;
291 }
292
293
294
295
296
297
298 public void setContentFilter(String contentFilter) {
299 this.contentFilter = contentFilter;
300 }
301
302
303
304
305
306
307
308 public void setExpandAll(String expandAll) {
309 if (expandAll.equalsIgnoreCase(EXPAND_SHOW)) {
310 this.expandAll = expandAll;
311 }
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 Content activePage = getCurrentActivePage();
359 try {
360 while (!ItemType.PAGE.getSystemName().equals(activePage.getNodeTypeName()) && activePage.getLevel() != 0) {
361 activePage = activePage.getParent();
362 }
363 } catch (RepositoryException e) {
364 log.error("Failed to obtain parent page for " + getCurrentActivePage().getHandle(), e);
365 activePage = getCurrentActivePage();
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.getLevel();
385
386
387 if (relativeLevels) {
388 this.startLevel += activePageLevel;
389 this.endLevel += activePageLevel;
390 }
391 if (this.startLevel <= activePageLevel) {
392 Content startContent = 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 NestableRuntimeException(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(Content page, Content activePage, JspWriter out) throws IOException, RepositoryException {
435
436 Collection<Content> children = new ArrayList<Content>(page.getChildren(ItemType.CONTENT));
437
438 if (children.size() == 0) {
439 return;
440 }
441
442 out.print("<ul class=\"level");
443 out.print(page.getLevel());
444 if (style != null && page.getLevel() == startLevel) {
445 out.print(" ");
446 out.print(style);
447 }
448 out.print("\">");
449
450 Iterator<Content> iter = children.iterator();
451
452 while (iter.hasNext()) {
453 final Content child = iter.next();
454
455 if (expandAll.equalsIgnoreCase(EXPAND_NONE) || expandAll.equalsIgnoreCase(EXPAND_SHOW)) {
456 if (child
457 .getNodeData(StringUtils.defaultString(this.hideInNav, DEFAULT_HIDEINNAV_NODEDATA)).getBoolean()) {
458 iter.remove();
459 continue;
460 }
461
462 if (filter != null) {
463 if (!filter.accept(child)) {
464 iter.remove();
465 continue;
466 }
467 }
468 } else {
469 if (child.getNodeData(StringUtils.defaultString(this.hideInNav, DEFAULT_HIDEINNAV_NODEDATA)).getBoolean()) {
470 iter.remove();
471 continue;
472 }
473 }
474 }
475
476 boolean isFirst = true;
477 Iterator<Content> visibleIt = children.iterator();
478 while (visibleIt.hasNext()) {
479 Content child = visibleIt.next();
480 List<String> cssClasses = new ArrayList<String>(4);
481
482 NodeData nodeData = I18nContentSupportFactory.getI18nSupport().getNodeData(child, NODEDATA_NAVIGATIONTITLE);
483 String title = null;
484 if(nodeData != null){
485 title = nodeData.getString(StringUtils.EMPTY);
486 }
487
488
489 if (StringUtils.isEmpty(title)) {
490 title = child.getTitle();
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.getHandle().equals(child.getHandle())) {
506
507 showChildren = true;
508 self = true;
509 cssClasses.add(CSS_LI_ACTIVE);
510 }
511 else if (!showChildren) {
512 showChildren = child.getLevel() <= activePage.getAncestors().size() && StringUtils.equals(activePage.getAncestor(child.getLevel()).getHandle(), child.getHandle());
513 }
514
515 if (!showChildren) {
516 showChildren = child
517 .getNodeData(StringUtils.defaultString(this.openMenu, DEFAULT_OPENMENU_NODEDATA))
518 .getBoolean();
519 }
520
521 if (endLevel > 0) {
522 showChildren &= child.getLevel() < endLevel;
523 }
524
525 cssClasses.add(hasVisibleChildren(child) ? showChildren ? CSS_LI_OPEN : CSS_LI_CLOSED : CSS_LI_LEAF);
526
527 if (child.getLevel() < activePage.getLevel()
528 && activePage.getAncestor(child.getLevel()).getHandle().equals(child.getHandle())) {
529 cssClasses.add(CSS_LI_TRAIL);
530 }
531
532 if (StringUtils.isNotEmpty(classProperty) && child.hasNodeData(classProperty)) {
533 cssClasses.add(child.getNodeData(classProperty).getString(StringUtils.EMPTY));
534 }
535
536 if (markFirstAndLastElement && isFirst) {
537 cssClasses.add(CSS_LI_FIRST);
538 isFirst = false;
539 }
540
541 if (markFirstAndLastElement && !visibleIt.hasNext()) {
542 cssClasses.add(CSS_LI_LAST);
543 }
544
545 StringBuffer css = new StringBuffer(cssClasses.size() * 10);
546 Iterator<String> iterator = cssClasses.iterator();
547 while (iterator.hasNext()) {
548 css.append(iterator.next());
549 if (iterator.hasNext()) {
550 css.append(" ");
551 }
552 }
553
554 out.print("<li class=\"");
555 out.print(css.toString());
556 out.print("\">");
557
558 if (self) {
559 out.println("<strong>");
560 }
561
562 String accesskey = null;
563 if(child.getNodeData(NODEDATA_ACCESSKEY) != null){
564 accesskey = child.getNodeData(NODEDATA_ACCESSKEY).getString(StringUtils.EMPTY);
565 }
566
567 out.print("<a href=\"");
568 out.print(((HttpServletRequest) this.pageContext.getRequest()).getContextPath());
569 out.print(I18nContentSupportFactory.getI18nSupport().toI18NURI(child.getHandle()));
570 out.print(".html\"");
571
572 if (StringUtils.isNotEmpty(accesskey)) {
573 out.print(" accesskey=\"");
574 out.print(accesskey);
575 out.print("\"");
576 }
577
578 if (nofollow != null && child.getNodeData(this.nofollow).getBoolean())
579 {
580 out.print(" rel=\"nofollow\"");
581 }
582
583 out.print(">");
584
585 if (StringUtils.isNotEmpty(this.wrapperElement)) {
586 out.print("<" + this.wrapperElement + ">");
587 }
588
589 out.print(StringEscapeUtils.escapeHtml(title));
590
591 if (StringUtils.isNotEmpty(this.wrapperElement)) {
592 out.print("</" + this.wrapperElement + ">");
593 }
594
595 out.print(" </a>");
596
597 if (self) {
598 out.println("</strong>");
599 }
600
601 if (showChildren) {
602 drawChildren(child, activePage, out);
603 }
604 out.print("</li>");
605 }
606
607 out.print("</ul>");
608 }
609
610
611
612
613
614
615
616 private boolean hasVisibleChildren(Content page) {
617 Collection<Content> children = page.getChildren();
618 if (children.size() > 0 && expandAll.equalsIgnoreCase(EXPAND_ALL)) {
619 return true;
620 }
621 for (Content ch : children) {
622 if (!ch.getNodeData(StringUtils.defaultString(this.hideInNav, DEFAULT_HIDEINNAV_NODEDATA)).getBoolean()) {
623 return true;
624 }
625 }
626 return false;
627 }
628
629 protected Node getCurrentActivePageNode() {
630 Node currentActpage = MgnlContext.getAggregationState().getCurrentContentNode();
631 if (currentActpage == null) {
632 currentActpage = MgnlContext.getAggregationState().getMainContentNode();
633 }
634 return currentActpage;
635 }
636
637
638
639
640 protected Content getCurrentActivePage() {
641 return ContentUtil.asContent(getCurrentActivePageNode());
642 }
643 }