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