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 * version 2.1 of the License, or (at your option) any later version. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * For further information about Alkacon Software GmbH & Co. KG, please see the 017 * company website: http://www.alkacon.com 018 * 019 * For further information about OpenCms, please see the 020 * project website: http://www.opencms.org 021 * 022 * You should have received a copy of the GNU Lesser General Public 023 * License along with this library; if not, write to the Free Software 024 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 025 */ 026 027package org.opencms.workplace.help; 028 029import org.opencms.file.CmsResource; 030import org.opencms.jsp.CmsJspActionElement; 031import org.opencms.jsp.CmsJspNavElement; 032import org.opencms.main.CmsIllegalArgumentException; 033 034import java.util.List; 035import java.util.StringTokenizer; 036 037import javax.servlet.http.HttpServletRequest; 038import javax.servlet.http.HttpServletResponse; 039import javax.servlet.jsp.PageContext; 040 041/** 042 * Generates a simple TOC - list by using a navigation model 043 * obtained from a <code>{@link org.opencms.jsp.CmsJspNavBuilder}</code>. <p> 044 * 045 * This is a simpler facade to a fixed html - list based layout. 046 * Only a navigation root path and a desired depth have to be set. 047 * It is not specific to the online help.<p> 048 * 049 * @since 6.0.0 050 */ 051public final class CmsHelpNavigationListView { 052 053 /** The depth (in levels) of the navigation. **/ 054 private int m_depth; 055 056 /** The CmsJspActionElement to use. **/ 057 private CmsJspActionElement m_jsp; 058 059 /** The root path where the navigation will start. **/ 060 private String m_navRootPath; 061 062 /** 063 * Creates a <code>CmsNaviationListView</code> which uses the given 064 * <code>CmsJspActionElement</code> for accessing the underlying 065 * navigation API. <p> 066 * 067 * @param jsp the <code>CmsJspActionElement</code> to use 068 */ 069 public CmsHelpNavigationListView(CmsJspActionElement jsp) { 070 071 m_jsp = jsp; 072 m_navRootPath = m_jsp.getCmsObject().getRequestContext().getUri(); 073 074 } 075 076 /** 077 * Creates a <code>CmsNaviationListView</code> which creates a 078 * <code>CmsJspActionElement</code> for accessing the underlying 079 * navigation API with the given arguments . <p> 080 * 081 * @param context the <code>PageContext</code> to use 082 * @param request the <code>HttpServletRequest</code> to use 083 * @param response the <code>HttpServletResponse</code> to use 084 * 085 */ 086 public CmsHelpNavigationListView(PageContext context, HttpServletRequest request, HttpServletResponse response) { 087 088 this(new CmsJspActionElement(context, request, response)); 089 } 090 091 /** 092 * Returns a String of spaces.<p> 093 * 094 * @param n the count of spaces 095 * 096 * @return a String of spaces 097 */ 098 private static String getSpaces(int n) { 099 100 // avoid negative NegativeArraySizeException in case uri is missing 101 n = Math.max(n, 0); 102 StringBuffer result = new StringBuffer(n); 103 for (; n > 0; n--) { 104 result.append(' '); 105 } 106 return result.toString(); 107 } 108 109 /** 110 * Returns a string containing the navigation created by using the internal members.<p> 111 * 112 * The navigation is a nested html list. <p> 113 * 114 * @return a string containing the navigation created by using the internal members 115 */ 116 public String createNavigation() { 117 118 StringBuffer buffer = new StringBuffer(2048); 119 int endlevel = calculateEndLevel(); 120 String spaces = getSpaces((endlevel - m_depth) * 2); 121 if (m_navRootPath != null) { 122 buffer.append("\n").append(spaces).append("<p>\n"); 123 buffer.append(spaces).append(" <ul>\n"); 124 List<CmsJspNavElement> navElements = m_jsp.getNavigation().getSiteNavigation(m_navRootPath, endlevel); 125 if (navElements.size() > 0) { 126 createNavigationInternal(buffer, navElements); 127 } 128 buffer.append(spaces).append(" </ul>\n"); 129 buffer.append(spaces).append("</p>"); 130 return buffer.toString(); 131 } else { 132 CmsIllegalArgumentException ex = new CmsIllegalArgumentException( 133 Messages.get().container(Messages.GUI_HELP_ERR_SITEMAP_MISSING_PARAM_1, "navRootPath")); 134 throw ex; 135 } 136 } 137 138 /** 139 * Returns the depth in levels of the navigation. <p> 140 * 141 * @return the depth in levels of the navigation. 142 */ 143 public int getDepth() { 144 145 return m_depth; 146 } 147 148 /** 149 * Returns the navigation root path of the navigation to generate a view for. <p> 150 * 151 * @return the navigation root path of the navigation to generate a view for. 152 */ 153 public String getSiteRootPath() { 154 155 return m_navRootPath; 156 } 157 158 /** 159 * Set the depth in level of the navigation to generate a view for. <p> 160 * 161 * @param depth the depth in level of the navigation to generate a view for to set 162 */ 163 public void setDepth(int depth) { 164 165 m_depth = depth; 166 } 167 168 /** 169 * Set the navigation root path of the navigation to generate a view for. <p> 170 * 171 * The navigation will start there. <p> 172 * 173 * @param navRootPath the navigation root path of the navigation to generate a view for to set 174 */ 175 public void setNavigationRootPath(String navRootPath) { 176 177 m_navRootPath = navRootPath; 178 } 179 180 /** 181 * Calculates and returns the navigation end level.<p> 182 * 183 * @return the navigation end level 184 */ 185 private int calculateEndLevel() { 186 187 int result = 0; 188 if (m_navRootPath != null) { 189 // where are we? (start level) 190 191 StringTokenizer counter = new StringTokenizer(m_navRootPath, "/", false); 192 // one less as level 0 nav elements accepted is one level (depth 1). 193 result = counter.countTokens() - 1; 194 if (!CmsResource.isFolder(m_navRootPath)) { 195 // cut stuff like system/workpalce/locale/de/help/index.html 196 result--; 197 } 198 result += m_depth; 199 } 200 if (result < 0) { 201 result = 0; 202 } 203 return result; 204 } 205 206 /** 207 * Creates the HTML for the internal help.<p> 208 * 209 * @param buffer the StringBuffer to which the Navigation will be appended 210 * @param navElements the navigation elements to build the navigation for 211 */ 212 private void createNavigationInternal(StringBuffer buffer, List<CmsJspNavElement> navElements) { 213 214 // take the element to render. 215 CmsJspNavElement element = navElements.remove(0); 216 int elementLevel = element.getNavTreeLevel(); 217 String spacing = getSpaces(elementLevel * 2); 218 // render element: 219 buffer.append(spacing).append("<li>\n"); 220 buffer.append(spacing).append(" <a href=\""); 221 buffer.append(m_jsp.link(element.getResourceName())); 222 buffer.append("\" title=\""); 223 buffer.append(element.getNavText()); 224 buffer.append("\""); 225 if (elementLevel == 1) { 226 buffer.append(" class=\"bold\""); 227 } 228 buffer.append(">"); 229 buffer.append(element.getNavText()); 230 buffer.append("</a>\n"); 231 232 // peek at the next (list is depth - first by contract) 233 if (!navElements.isEmpty()) { 234 CmsJspNavElement child = navElements.get(0); 235 int childLevel = child.getNavTreeLevel(); 236 if (elementLevel < childLevel) { 237 // next one goes down a level: it is a child by tree means 238 buffer.append(spacing).append(" <ul>\n"); 239 } else if (elementLevel == childLevel) { 240 // it is a sibling: close our list item, no recursion 241 buffer.append(spacing).append("</li>\n"); 242 } else { 243 // next element gets up one layer 244 // this has to happen because of the depth-first contract! 245 buffer.append(spacing).append(" </li>\n").append(spacing).append("</ul>\n"); 246 } 247 createNavigationInternal(buffer, navElements); 248 } else { 249 // no more next elements: get back and close all lists (by using the recursion we are in) 250 buffer.append(spacing).append(" </li>\n").append(spacing).append("</ul>\n"); 251 } 252 253 } 254}