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