001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.jsp; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsProperty; 032import org.opencms.file.CmsPropertyDefinition; 033import org.opencms.file.CmsResource; 034import org.opencms.file.CmsResourceFilter; 035import org.opencms.file.types.CmsResourceTypeFolder; 036import org.opencms.file.types.I_CmsResourceType; 037import org.opencms.main.CmsException; 038import org.opencms.main.CmsLog; 039import org.opencms.main.OpenCms; 040import org.opencms.util.CmsFileUtil; 041 042import java.util.ArrayList; 043import java.util.Collections; 044import java.util.List; 045import java.util.Locale; 046import java.util.Map; 047 048import org.apache.commons.logging.Log; 049 050/** 051 * Bean to provide a convenient way to build navigation structures based on the 052 * <code>{@link org.opencms.jsp.CmsJspNavElement}</code>.<p> 053 * 054 * Use this together with the <code>{@link org.opencms.jsp.CmsJspActionElement}</code> 055 * to obtain navigation information based on the current users permissions. 056 * For example, use <code>{@link #getNavigationForFolder(String)}</code> and pass the 057 * value of the current OpenCms user context uri obtained 058 * from <code>{@link org.opencms.file.CmsRequestContext#getUri()}</code> as argument to obtain a list 059 * of all items in the navigation of the current folder. Then use a simple scriptlet to 060 * iterate over these items and create a HTML navigation.<p> 061 * 062 * @since 6.0.0 063 * 064 * @see org.opencms.jsp.CmsJspNavElement 065 */ 066public class CmsJspNavBuilder { 067 068 /** 069 * Navigation builder context. 070 * 071 * Stored in nav elements so they can ask the navigation builder for sub-navigation entries, with appropriate resource filter etc. 072 */ 073 public static class NavContext { 074 075 /** The resource filter used. */ 076 private CmsResourceFilter m_filter; 077 078 /** The nav builder used. */ 079 private CmsJspNavBuilder m_navBuilder; 080 081 /** The visibility option used. */ 082 private Visibility m_visibility; 083 084 /** 085 * Creates a new instance. 086 * 087 * @param navBuilder the navigation builder 088 * @param visibility the visibility 089 * @param filter the resource filter 090 */ 091 public NavContext(CmsJspNavBuilder navBuilder, Visibility visibility, CmsResourceFilter filter) { 092 093 super(); 094 m_visibility = visibility; 095 m_filter = filter; 096 m_navBuilder = navBuilder; 097 } 098 099 /** 100 * Gets the resource filter. 101 * 102 * @return the resource filter 103 */ 104 public CmsResourceFilter getFilter() { 105 106 return m_filter; 107 } 108 109 /** 110 * Gets the navigation builder. 111 * 112 * @return the navigation builder 113 */ 114 public CmsJspNavBuilder getNavBuilder() { 115 116 return m_navBuilder; 117 } 118 119 /** 120 * Gets the visibility setting. 121 * 122 * @return the visibility setting 123 */ 124 public Visibility getVisibility() { 125 126 return m_visibility; 127 } 128 129 } 130 131 /** The visibility mode. */ 132 public static enum Visibility { 133 /** All entries. */ 134 all, 135 /** Navigation including hidden entries. */ 136 includeHidden, 137 /** Navigation only. */ 138 navigation 139 } 140 141 /** Default file property value to mark navigation level folders. */ 142 public static final String NAVIGATION_LEVEL_FOLDER = "##navigation_level_folder##"; 143 144 /** The log object for this class. */ 145 private static final Log LOG = CmsLog.getLog(CmsJspNavBuilder.class); 146 147 /** The current CMS context. */ 148 protected CmsObject m_cms; 149 150 /** The locale for which the property should be read. */ 151 protected Locale m_locale; 152 153 /** The current request URI. */ 154 protected String m_requestUri; 155 156 /** The current request folder. */ 157 protected String m_requestUriFolder; 158 159 /** 160 * Empty constructor, so that this bean can be initialized from a JSP.<p> 161 */ 162 public CmsJspNavBuilder() { 163 164 // empty 165 } 166 167 /** 168 * Default constructor.<p> 169 * 170 * @param cms context provider for the current request 171 */ 172 public CmsJspNavBuilder(CmsObject cms) { 173 174 init(cms, null); 175 } 176 177 /** 178 * Constructor for a version that reads properties according to a locale.<p> 179 * 180 * @param cms context provider for the current request 181 * @param locale the locale for which properties should be accessed 182 */ 183 public CmsJspNavBuilder(CmsObject cms, Locale locale) { 184 185 init(cms, locale); 186 } 187 188 /** 189 * Returns the full name (including VFS path) of the default file for this navigation element 190 * or <code>null</code> if the navigation element is not a folder.<p> 191 * 192 * The default file of a folder is determined by the value of the property 193 * <code>default-file</code> or the system wide property setting.<p> 194 * 195 * @param cms the CMS object 196 * @param folder full name of the folder 197 * 198 * @return the name of the default file 199 * 200 * @deprecated use {@link CmsObject#readDefaultFile(String)} instead 201 */ 202 @Deprecated 203 public static String getDefaultFile(CmsObject cms, String folder) { 204 205 if (folder.endsWith("/")) { 206 try { 207 CmsResource defaultFile = cms.readDefaultFile(folder); 208 if (defaultFile != null) { 209 return cms.getSitePath(defaultFile); 210 } 211 } catch (CmsException e) { 212 LOG.debug(e.getLocalizedMessage(), e); 213 } 214 return folder; 215 } 216 return null; 217 } 218 219 /** 220 * Collect all navigation elements from the files in the given folder, 221 * navigation elements are of class {@link CmsJspNavElement}.<p> 222 * 223 * @param cms context provider for the current request 224 * @param folder the selected folder 225 * 226 * @return a sorted (ascending to navigation position) list of navigation elements 227 * 228 * @deprecated use {@link #getNavigationForFolder(String)} instead 229 */ 230 @Deprecated 231 public static List<CmsJspNavElement> getNavigationForFolder(CmsObject cms, String folder) { 232 233 return new CmsJspNavBuilder(cms).getNavigationForFolder(folder); 234 } 235 236 /** 237 * Build a navigation for the folder that is either minus levels up 238 * from the given folder, or that is plus levels down from the 239 * root folder towards the given folder.<p> 240 * 241 * If level is set to zero the root folder is used by convention.<p> 242 * 243 * @param cms context provider for the current request 244 * @param folder the selected folder 245 * @param level if negative, walk this many levels up, if positive, walk this many 246 * levels down from root folder 247 * 248 * @return a sorted (ascending to navigation position) list of navigation elements 249 * 250 * @deprecated use {@link #getNavigationForFolder(String, int)} instead 251 */ 252 @Deprecated 253 public static List<CmsJspNavElement> getNavigationForFolder(CmsObject cms, String folder, int level) { 254 255 return new CmsJspNavBuilder(cms).getNavigationForFolder(folder, level); 256 } 257 258 /** 259 * Returns a navigation element for the named resource.<p> 260 * 261 * @param cms context provider for the current request 262 * @param resource the resource name to get the navigation information for, 263 * must be a full path name, e.g. "/docs/index.html" 264 * 265 * @return a navigation element for the given resource 266 * 267 * @deprecated use {@link #getNavigationForResource(String)} instead 268 */ 269 @Deprecated 270 public static CmsJspNavElement getNavigationForResource(CmsObject cms, String resource) { 271 272 return new CmsJspNavBuilder(cms).getNavigationForResource(resource); 273 } 274 275 /** 276 * Builds a tree navigation for the folders between the provided start and end level.<p> 277 * 278 * A tree navigation includes all navigation elements that are required to display a tree structure. 279 * However, the data structure is a simple list. 280 * Each of the navigation elements in the list has the {@link CmsJspNavElement#getNavTreeLevel()} set 281 * to the level it belongs to. Use this information to distinguish between the navigation levels.<p> 282 * 283 * @param cms context provider for the current request 284 * @param folder the selected folder 285 * @param startlevel the start level 286 * @param endlevel the end level 287 * 288 * @return a sorted list of navigation elements with the navigation tree level property set 289 * 290 * @deprecated use {@link #getNavigationForResource(String)} instead 291 */ 292 @Deprecated 293 public static List<CmsJspNavElement> getNavigationTreeForFolder( 294 CmsObject cms, 295 String folder, 296 int startlevel, 297 int endlevel) { 298 299 return new CmsJspNavBuilder(cms).getNavigationTreeForFolder(folder, startlevel, endlevel); 300 } 301 302 /** 303 * This method builds a complete navigation tree with entries of all branches 304 * from the specified folder.<p> 305 * 306 * For an unlimited depth of the navigation (i.e. no <code>endLevel</code>), 307 * set the <code>endLevel</code> to a value < 0.<p> 308 * 309 * 310 * @param cms the current CMS context 311 * @param folder the root folder of the navigation tree 312 * @param endLevel the end level of the navigation 313 * 314 * @return list of navigation elements, in depth first order 315 * 316 * @deprecated use {@link #getNavigationForResource(String)} instead 317 */ 318 @Deprecated 319 public static List<CmsJspNavElement> getSiteNavigation(CmsObject cms, String folder, int endLevel) { 320 321 return new CmsJspNavBuilder(cms).getSiteNavigation(folder, endLevel); 322 } 323 324 /** 325 * Returns whether the given resource is a folder and is marked to be a navigation level folder.<p> 326 * 327 * @param cms the cms context 328 * @param resource the resource 329 * 330 * @return <code>true</code> if the resource is marked to be a navigation level folder 331 */ 332 public static boolean isNavLevelFolder(CmsObject cms, CmsResource resource) { 333 334 if (resource.isFolder()) { 335 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(resource); 336 if (CmsResourceTypeFolder.RESOURCE_TYPE_NAME.equals(type.getTypeName())) { 337 try { 338 CmsProperty prop = cms.readPropertyObject( 339 resource, 340 CmsPropertyDefinition.PROPERTY_DEFAULT_FILE, 341 false); 342 return !prop.isNullProperty() && NAVIGATION_LEVEL_FOLDER.equals(prop.getValue()); 343 } catch (CmsException e) { 344 LOG.debug(e.getMessage(), e); 345 } 346 } 347 } 348 return false; 349 } 350 351 /** 352 * Gets the CMS context used for building the navigation. 353 * 354 * @return the CMS context 355 */ 356 public CmsObject getCmsObject() { 357 358 return m_cms; 359 } 360 361 /** 362 * Build a "bread crumb" path navigation to the current folder.<p> 363 * 364 * @return ArrayList sorted list of navigation elements 365 * 366 * @see #getNavigationBreadCrumb(String, int, int, boolean) 367 */ 368 public List<CmsJspNavElement> getNavigationBreadCrumb() { 369 370 return getNavigationBreadCrumb(m_requestUriFolder, 0, -1, true); 371 } 372 373 /** 374 * Build a "bread crumb" path navigation to the current folder.<p> 375 * 376 * @param startlevel the start level, if negative, go down |n| steps from selected folder 377 * @param currentFolder include the selected folder in navigation or not 378 * 379 * @return sorted list of navigation elements 380 * 381 * @see #getNavigationBreadCrumb(String, int, int, boolean) 382 */ 383 public List<CmsJspNavElement> getNavigationBreadCrumb(int startlevel, boolean currentFolder) { 384 385 return getNavigationBreadCrumb(m_requestUriFolder, startlevel, -1, currentFolder); 386 } 387 388 /** 389 * Build a "bread crumb" path navigation to the current folder.<p> 390 * 391 * @param startlevel the start level, if negative, go down |n| steps from selected folder 392 * @param endlevel the end level, if -1, build navigation to selected folder 393 * 394 * @return sorted list of navigation elements 395 * 396 * @see #getNavigationBreadCrumb(String, int, int, boolean) 397 */ 398 public List<CmsJspNavElement> getNavigationBreadCrumb(int startlevel, int endlevel) { 399 400 return getNavigationBreadCrumb(m_requestUriFolder, startlevel, endlevel, true); 401 } 402 403 /** 404 * Build a "bread crumb" path navigation to the given folder.<p> 405 * 406 * The <code>startlevel</code> marks the point where the navigation starts from, if negative, 407 * the count of steps to go down from the given folder.<p> 408 * 409 * The <code>endlevel</code> is the maximum level of the navigation path, set it to -1 to build the 410 * complete navigation to the given folder.<p> 411 * 412 * You can include the given folder in the navigation by setting <code>currentFolder</code> to 413 * <code>true</code>, otherwise <code>false</code>.<p> 414 * 415 * @param folder the selected folder 416 * @param startlevel the start level, if negative, go down |n| steps from selected folder 417 * @param endlevel the end level, if -1, build navigation to selected folder 418 * @param currentFolder include the selected folder in navigation or not 419 * 420 * @return sorted list of navigation elements 421 */ 422 public List<CmsJspNavElement> getNavigationBreadCrumb( 423 String folder, 424 int startlevel, 425 int endlevel, 426 boolean currentFolder) { 427 428 List<CmsJspNavElement> result = new ArrayList<CmsJspNavElement>(); 429 430 int level = CmsResource.getPathLevel(folder); 431 // decrease folder level if current folder is not displayed 432 if (!currentFolder) { 433 level -= 1; 434 } 435 // check current level and change endlevel if it is higher or -1 436 if ((level < endlevel) || (endlevel == -1)) { 437 endlevel = level; 438 } 439 440 // if startlevel is negative, display only |startlevel| links 441 if (startlevel < 0) { 442 startlevel = endlevel + startlevel + 1; 443 if (startlevel < 0) { 444 startlevel = 0; 445 } 446 } 447 448 // create the list of navigation elements 449 for (int i = startlevel; i <= endlevel; i++) { 450 String navFolder = CmsResource.getPathPart(folder, i); 451 CmsJspNavElement e = getNavigationForResource(navFolder); 452 if (e != null) { 453 // add element to list 454 result.add(e); 455 } 456 } 457 458 return result; 459 } 460 461 /** 462 * Collect all navigation elements from the files of the folder of the current request URI.<p> 463 * 464 * @return a sorted (ascending to navigation position) list of navigation elements 465 */ 466 public List<CmsJspNavElement> getNavigationForFolder() { 467 468 return getNavigationForFolder(m_requestUriFolder); 469 } 470 471 /** 472 * Build a navigation for the folder that is either minus levels up 473 * from of the folder of the current request URI, or that is plus levels down from the 474 * root folder towards the current request URI.<p> 475 * 476 * If level is set to zero the root folder is used by convention.<p> 477 * 478 * @param level if negative, walk this many levels up, if positive, walk this many 479 * levels down from root folder 480 * @return a sorted (ascending to navigation position) list of navigation elements 481 */ 482 public List<CmsJspNavElement> getNavigationForFolder(int level) { 483 484 return getNavigationForFolder(m_requestUriFolder, level); 485 } 486 487 /** 488 * Collect all navigation visible elements from the files in the given folder.<p> 489 * 490 * @param folder the selected folder 491 * 492 * @return A sorted (ascending to navigation position) list of navigation elements 493 */ 494 public List<CmsJspNavElement> getNavigationForFolder(String folder) { 495 496 return getNavigationForFolder(folder, Visibility.navigation, CmsResourceFilter.DEFAULT); 497 } 498 499 /** 500 * Build a navigation for the folder that is either minus levels up 501 * from the given folder, or that is plus levels down from the 502 * root folder towards the given folder.<p> 503 * 504 * If level is set to zero the root folder is used by convention.<p> 505 * 506 * @param folder the selected folder 507 * @param level if negative, walk this many levels up, if positive, walk this many 508 * levels down from root folder 509 * 510 * @return a sorted (ascending to navigation position) list of navigation elements 511 */ 512 public List<CmsJspNavElement> getNavigationForFolder(String folder, int level) { 513 514 folder = CmsResource.getFolderPath(folder); 515 // If level is one just use root folder 516 if (level == 0) { 517 return getNavigationForFolder("/"); 518 } 519 String navfolder = CmsResource.getPathPart(folder, level); 520 // If navigation folder found use it to build navigation 521 if (navfolder != null) { 522 return getNavigationForFolder(navfolder); 523 } 524 // Nothing found, return empty list 525 return Collections.<CmsJspNavElement> emptyList(); 526 } 527 528 /** 529 * Collect all navigation elements from the files in the given folder.<p> 530 * 531 * @param folder the selected folder 532 * @param visibility the visibility mode 533 * @param resourceFilter the filter to use reading the resources 534 * 535 * @return A sorted (ascending to navigation position) list of navigation elements 536 */ 537 public List<CmsJspNavElement> getNavigationForFolder( 538 String folder, 539 Visibility visibility, 540 CmsResourceFilter resourceFilter) { 541 542 folder = CmsFileUtil.removeTrailingSeparator(folder); 543 List<CmsJspNavElement> result = new ArrayList<CmsJspNavElement>(); 544 545 List<CmsResource> resources = null; 546 try { 547 548 resources = m_cms.getResourcesInFolder(folder, resourceFilter); 549 } catch (Exception e) { 550 // should never happen 551 LOG.error(e.getLocalizedMessage(), e); 552 } 553 if (resources == null) { 554 return Collections.<CmsJspNavElement> emptyList(); 555 } 556 boolean includeAll = visibility == Visibility.all; 557 boolean includeHidden = visibility == Visibility.includeHidden; 558 for (CmsResource r : resources) { 559 CmsJspNavElement element = getNavigationForResource(m_cms.getSitePath(r), resourceFilter); 560 if ((element != null) 561 && (includeAll 562 || (element.isInNavigation() && (includeHidden || !element.isHiddenNavigationEntry())))) { 563 element.setNavContext(new NavContext(this, visibility, resourceFilter)); 564 result.add(element); 565 } 566 } 567 Collections.sort(result); 568 return result; 569 } 570 571 /** 572 * Returns a navigation element for the resource of the current request URI.<p> 573 * 574 * @return a navigation element for the resource of the current request URI 575 */ 576 public CmsJspNavElement getNavigationForResource() { 577 578 return getNavigationForResource(m_requestUri); 579 } 580 581 /** 582 * Returns a navigation element for the named resource.<p> 583 * 584 * @param sitePath the resource name to get the navigation information for, 585 * must be a full path name, e.g. "/docs/index.html" 586 * 587 * @return a navigation element for the given resource 588 */ 589 public CmsJspNavElement getNavigationForResource(String sitePath) { 590 591 CmsJspNavElement result = getNavigationForResource(sitePath, CmsResourceFilter.DEFAULT, false); 592 if ((result != null) && (result.getNavContext() == null)) { 593 result.setNavContext(new NavContext(this, Visibility.navigation, CmsResourceFilter.DEFAULT)); 594 } 595 return result; 596 } 597 598 /** 599 * Returns a navigation element for the named resource.<p> 600 * 601 * @param sitePath the resource name to get the navigation information for, 602 * must be a full path name, e.g. "/docs/index.html" 603 * @param reourceFilter the resource filter 604 * 605 * @return a navigation element for the given resource 606 */ 607 public CmsJspNavElement getNavigationForResource(String sitePath, CmsResourceFilter reourceFilter) { 608 609 return getNavigationForResource(sitePath, reourceFilter, false); 610 } 611 612 /** 613 * Builds a tree navigation for the folders between the provided start and end level.<p> 614 * 615 * @param startlevel the start level 616 * @param endlevel the end level 617 * 618 * @return a sorted list of navigation elements with the navigation tree level property set 619 * 620 * @see #getNavigationTreeForFolder(String, int, int) 621 */ 622 public List<CmsJspNavElement> getNavigationTreeForFolder(int startlevel, int endlevel) { 623 624 return getNavigationTreeForFolder(m_requestUriFolder, startlevel, endlevel); 625 } 626 627 /** 628 * Builds a tree navigation for the folders between the provided start and end level.<p> 629 * 630 * @param folder the selected folder 631 * @param startlevel the start level 632 * @param endlevel the end level 633 * 634 * @return a sorted list of navigation elements with the navigation tree level property set 635 */ 636 public List<CmsJspNavElement> getNavigationTreeForFolder(String folder, int startlevel, int endlevel) { 637 638 folder = CmsResource.getFolderPath(folder); 639 // Make sure start and end level make sense 640 if (endlevel < startlevel) { 641 return Collections.<CmsJspNavElement> emptyList(); 642 } 643 int currentlevel = CmsResource.getPathLevel(folder); 644 if (currentlevel < endlevel) { 645 endlevel = currentlevel; 646 } 647 if (startlevel == endlevel) { 648 return getNavigationForFolder(CmsResource.getPathPart(folder, startlevel), startlevel); 649 } 650 651 List<CmsJspNavElement> result = new ArrayList<CmsJspNavElement>(); 652 float parentcount = 0; 653 654 for (int i = startlevel; i <= endlevel; i++) { 655 String currentfolder = CmsResource.getPathPart(folder, i); 656 List<CmsJspNavElement> entries = getNavigationForFolder(currentfolder); 657 // Check for parent folder 658 if (parentcount > 0) { 659 for (CmsJspNavElement e : entries) { 660 e.setNavPosition(e.getNavPosition() + parentcount); 661 } 662 } 663 // Add new entries to result 664 result.addAll(entries); 665 Collections.sort(result); 666 // Finally spread the values of the navigation items so that there is enough room for further items 667 float pos = 0; 668 int count = 0; 669 String nextfolder = CmsResource.getPathPart(folder, i + 1); 670 parentcount = 0; 671 for (CmsJspNavElement e : result) { 672 pos = 10000 * (++count); 673 e.setNavPosition(pos); 674 if (e.getResourceName().startsWith(nextfolder)) { 675 parentcount = pos; 676 } 677 } 678 if (parentcount == 0) { 679 parentcount = pos; 680 } 681 } 682 return result; 683 } 684 685 /** 686 * This method builds a complete site navigation tree with entries of all branches.<p> 687 * 688 * @see #getSiteNavigation(String, int) 689 * 690 * @return list of navigation elements, in depth first order 691 */ 692 public List<CmsJspNavElement> getSiteNavigation() { 693 694 return getSiteNavigation("/", Visibility.navigation, -1); 695 } 696 697 /** 698 * This method builds a complete navigation tree with entries of all branches 699 * from the specified folder.<p> 700 * 701 * @param folder folder the root folder of the navigation tree 702 * @param endLevel the end level of the navigation 703 * 704 * @return list of navigation elements, in depth first order 705 */ 706 public List<CmsJspNavElement> getSiteNavigation(String folder, int endLevel) { 707 708 return getSiteNavigation(folder, Visibility.navigation, endLevel); 709 } 710 711 /** 712 * This method builds a complete navigation tree with entries of all branches 713 * from the specified folder.<p> 714 * 715 * @param folder folder the root folder of the navigation tree 716 * @param visibility controls whether entries hidden from navigation or not in navigation at all should be included 717 * @param endLevel the end level of the navigation 718 * 719 * @return list of navigation elements, in depth first order 720 */ 721 public List<CmsJspNavElement> getSiteNavigation(String folder, Visibility visibility, int endLevel) { 722 723 folder = CmsFileUtil.addTrailingSeparator(folder); 724 // check if a specific end level was given, if not, build the complete navigation 725 boolean noLimit = false; 726 if (endLevel < 0) { 727 noLimit = true; 728 } 729 List<CmsJspNavElement> list = new ArrayList<CmsJspNavElement>(); 730 // get the navigation for this folder 731 List<CmsJspNavElement> curnav = getNavigationForFolder(folder, visibility, CmsResourceFilter.DEFAULT); 732 // loop through all navigation entries 733 for (CmsJspNavElement ne : curnav) { 734 // add the navigation entry to the result list 735 list.add(ne); 736 // check if navigation entry is a folder or navigation level and below the max level -> if so, get the navigation from this folder as well 737 if ((ne.isFolderLink() || ne.isNavigationLevel()) && (noLimit || (ne.getNavTreeLevel() < endLevel))) { 738 List<CmsJspNavElement> subnav = getSiteNavigation( 739 m_cms.getSitePath(ne.getResource()), 740 visibility, 741 endLevel); 742 // copy the result of the subfolder to the result list 743 list.addAll(subnav); 744 } 745 } 746 return list; 747 } 748 749 /** 750 * Initializes this bean.<p> 751 * 752 * @param cms the current cms context 753 */ 754 public void init(CmsObject cms) { 755 756 init(cms, null, cms.getRequestContext().getUri()); 757 } 758 759 /** 760 * Initializes this bean.<p> 761 * 762 * @param cms the current cms context 763 * @param locale the locale for which properties should be read 764 */ 765 public void init(CmsObject cms, Locale locale) { 766 767 init(cms, locale, cms.getRequestContext().getUri()); 768 } 769 770 /** 771 * Initializes this bean.<p> 772 * 773 * @param cms the current cms context 774 * @param locale the locale for which properties should be read 775 * @param requestUri the request URI 776 */ 777 public void init(CmsObject cms, Locale locale, String requestUri) { 778 779 m_cms = cms; 780 m_locale = locale; 781 m_requestUri = requestUri; 782 m_requestUriFolder = CmsResource.getFolderPath(m_requestUri); 783 } 784 785 /** 786 * Collect all navigation elements from the files in the given folder.<p> 787 * 788 * @param folder the selected folder 789 * @param includeInvisible <code>true</code> to include elements not visible in navigation 790 * @param resourceFilter the filter to use reading the resources 791 * @param shallow <code>true</code> for a shallow look up, not regarding next level resources 792 * 793 * @return A sorted (ascending to navigation position) list of navigation elements 794 */ 795 private List<CmsJspNavElement> getNavigationForFolder( 796 String folder, 797 boolean includeInvisible, 798 CmsResourceFilter resourceFilter, 799 boolean shallow) { 800 801 folder = CmsResource.getFolderPath(folder); 802 List<CmsJspNavElement> result = new ArrayList<CmsJspNavElement>(); 803 804 List<CmsResource> resources; 805 try { 806 resources = m_cms.getResourcesInFolder(folder, resourceFilter); 807 } catch (Exception e) { 808 // should never happen 809 LOG.error(e.getLocalizedMessage(), e); 810 return Collections.<CmsJspNavElement> emptyList(); 811 } 812 813 for (CmsResource r : resources) { 814 CmsJspNavElement element = getNavigationForResource(m_cms.getSitePath(r), resourceFilter, shallow); 815 if ((element != null) && (includeInvisible || element.isInNavigation())) { 816 result.add(element); 817 } 818 } 819 Collections.sort(result); 820 return result; 821 } 822 823 /** 824 * Returns a navigation element for the named resource.<p> 825 * 826 * @param sitePath the resource name to get the navigation information for, 827 * must be a full path name, e.g. "/docs/index.html" 828 * @param resourceFilter the filter to use reading the resources 829 * @param shallow <code>true</code> for a shallow look up, not regarding next level resources 830 * 831 * @return a navigation element for the given resource 832 */ 833 private CmsJspNavElement getNavigationForResource( 834 String sitePath, 835 CmsResourceFilter resourceFilter, 836 boolean shallow) { 837 838 CmsResource resource; 839 Map<String, String> propertiesMap; 840 int level = CmsResource.getPathLevel(sitePath); 841 if (sitePath.endsWith("/")) { 842 level--; 843 } 844 try { 845 resource = m_cms.readResource(sitePath, resourceFilter); 846 List<CmsProperty> properties = m_cms.readPropertyObjects(resource, false); 847 propertiesMap = CmsProperty.toMap(properties); 848 if (resource.isFolder()) { 849 if (resourceFilter.equals(CmsResourceFilter.DEFAULT) 850 && !NAVIGATION_LEVEL_FOLDER.equals( 851 propertiesMap.get(CmsPropertyDefinition.PROPERTY_DEFAULT_FILE))) { 852 try { 853 CmsResource defaultFile = m_cms.readDefaultFile(resource, resourceFilter); 854 if ((defaultFile != null) 855 && !defaultFile.isReleasedAndNotExpired(m_cms.getRequestContext().getRequestTime())) { 856 // do not show navigation entries for unreleased or expired resources 857 return null; 858 } 859 } catch (@SuppressWarnings("unused") CmsException e) { 860 // may happen if permissions are not sufficient can be ignored 861 } 862 } 863 if (!sitePath.endsWith("/")) { 864 sitePath = sitePath + "/"; 865 } 866 if (!shallow 867 && (NAVIGATION_LEVEL_FOLDER.equals( 868 propertiesMap.get(CmsPropertyDefinition.PROPERTY_DEFAULT_FILE)))) { 869 // this folder is marked as a navigation level, set the site path to the first sub element 870 List<CmsJspNavElement> subElements = getNavigationForFolder(sitePath, false, resourceFilter, true); 871 if (!subElements.isEmpty()) { 872 CmsJspNavElement subElement = subElements.get(0); 873 subElement = getNavigationForResource(subElement.getSitePath(), resourceFilter, false); 874 sitePath = subElement.getSitePath(); 875 } 876 } 877 } 878 } catch (Exception e) { 879 // may happen if permissions are not sufficient 880 LOG.warn(e.getLocalizedMessage(), e); 881 return null; 882 } 883 884 return new CmsJspNavElement(sitePath, resource, propertiesMap, level, m_locale); 885 } 886}