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 @Override
353 public int doEndTag() throws JspException {
354 Content activePage = Resource.getCurrentActivePage();
355 try {
356 while (!ItemType.CONTENT.getSystemName().equals(activePage.getNodeTypeName()) && activePage.getParent() != null) {
357 activePage = activePage.getParent();
358 }
359 } catch (RepositoryException e) {
360 log.error("Failed to obtain parent page for " + Resource.getCurrentActivePage().getHandle(), e);
361 activePage = Resource.getCurrentActivePage();
362 }
363 JspWriter out = this.pageContext.getOut();
364
365 if (StringUtils.isNotEmpty(this.contentFilter)) {
366 try {
367 filter = (ContentFilter) this.pageContext.getAttribute(this.contentFilter);
368 }
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 }
394 catch (RepositoryException e) {
395 log.error("RepositoryException caught while drawing navigation: " + e.getMessage(), e);
396 return EVAL_PAGE;
397 }
398 catch (IOException e) {
399
400 throw new NestableRuntimeException(e);
401 }
402
403 return EVAL_PAGE;
404 }
405
406 @Override
407 public void release() {
408 this.startLevel = 0;
409 this.endLevel = 0;
410 this.hideInNav = null;
411 this.openMenu = null;
412 this.style = null;
413 this.classProperty = null;
414 this.expandAll = EXPAND_NONE;
415 this.relativeLevels = false;
416 this.wrapperElement = "";
417 this.contentFilter = "";
418 this.filter = null;
419 this.nofollow = null;
420 this.markFirstAndLastElement = false;
421 super.release();
422 }
423
424
425
426
427
428
429
430
431
432 private void drawChildren(Content page, Content activePage, JspWriter out) throws IOException, RepositoryException {
433
434 Collection<Content> children = new ArrayList<Content>(page.getChildren(ItemType.CONTENT));
435
436 if (children.size() == 0) {
437 return;
438 }
439
440 out.print("<ul class=\"level");
441 out.print(page.getLevel());
442 if (style != null && page.getLevel() == startLevel) {
443 out.print(" ");
444 out.print(style);
445 }
446 out.print("\">");
447
448 Iterator<Content> iter = children.iterator();
449
450 while(iter.hasNext()) {
451 final Content child = iter.next();
452
453 if (expandAll.equalsIgnoreCase(EXPAND_NONE) || expandAll.equalsIgnoreCase(EXPAND_SHOW)) {
454 if (child
455 .getNodeData(StringUtils.defaultString(this.hideInNav, DEFAULT_HIDEINNAV_NODEDATA)).getBoolean()) {
456 iter.remove();
457 continue;
458 }
459
460 if (filter != null) {
461 if (!filter.accept(child)) {
462 iter.remove();
463 continue;
464 }
465 }
466 } else {
467 if (child.getNodeData(StringUtils.defaultString(this.hideInNav, DEFAULT_HIDEINNAV_NODEDATA)).getBoolean()) {
468 iter.remove();
469 continue;
470 }
471 }
472 }
473
474 boolean isFirst = true;
475 Iterator<Content> visibleIt = children.iterator();
476 while (visibleIt.hasNext()) {
477 Content child = visibleIt.next();
478 List<String> cssClasses = new ArrayList<String>(4);
479
480 NodeData nodeData = I18nContentSupportFactory.getI18nSupport().getNodeData(child, NODEDATA_NAVIGATIONTITLE);
481 String title = nodeData.getString(StringUtils.EMPTY);
482
483
484 if (StringUtils.isEmpty(title)) {
485 title = child.getTitle();
486 }
487
488
489 if (StringUtils.isEmpty(title)) {
490 title = child.getName();
491 }
492
493 boolean showChildren = false;
494 boolean self = false;
495
496 if (!expandAll.equalsIgnoreCase(EXPAND_NONE)) {
497 showChildren = true;
498 }
499
500 if (activePage.getHandle().equals(child.getHandle())) {
501
502 showChildren = true;
503 self = true;
504 cssClasses.add(CSS_LI_ACTIVE);
505 }
506 else if (!showChildren) {
507 showChildren = (child.getLevel() <= activePage.getAncestors().size() && activePage.getAncestor(
508 child.getLevel()).getHandle().equals(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 = child.getNodeData(NODEDATA_ACCESSKEY).getString(StringUtils.EMPTY);
559
560 out.print("<a href=\"");
561 out.print(((HttpServletRequest) this.pageContext.getRequest()).getContextPath());
562 out.print(I18nContentSupportFactory.getI18nSupport().toI18NURI(child.getHandle()));
563 out.print(".html\"");
564
565 if (StringUtils.isNotEmpty(accesskey)) {
566 out.print(" accesskey=\"");
567 out.print(accesskey);
568 out.print("\"");
569 }
570
571 if (nofollow != null && child.getNodeData(this.nofollow).getBoolean())
572 {
573 out.print(" rel=\"nofollow\"");
574 }
575
576 out.print(">");
577
578 if (StringUtils.isNotEmpty(this.wrapperElement)) {
579 out.print("<" + this.wrapperElement + ">");
580 }
581
582 out.print(StringEscapeUtils.escapeHtml(title));
583
584 if (StringUtils.isNotEmpty(this.wrapperElement)) {
585 out.print("</" + this.wrapperElement + ">");
586 }
587
588 out.print(" </a>");
589
590 if (self) {
591 out.println("</strong>");
592 }
593
594 if (showChildren) {
595 drawChildren(child, activePage, out);
596 }
597 out.print("</li>");
598 }
599
600 out.print("</ul>");
601 }
602
603
604
605
606
607
608
609 private boolean hasVisibleChildren(Content page) {
610 Collection<Content> children = page.getChildren();
611 if (children.size() > 0 && expandAll.equalsIgnoreCase(EXPAND_ALL)) {
612 return true;
613 }
614 for (Content ch : children) {
615 if (!ch.getNodeData(StringUtils.defaultString(this.hideInNav, DEFAULT_HIDEINNAV_NODEDATA)).getBoolean()) {
616 return true;
617 }
618 }
619 return false;
620 }
621
622 }