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.CmsProperty; 031import org.opencms.flex.CmsFlexController; 032import org.opencms.i18n.CmsEncoder; 033import org.opencms.main.CmsException; 034import org.opencms.main.CmsLog; 035import org.opencms.staticexport.CmsLinkManager; 036import org.opencms.util.CmsStringUtil; 037 038import java.util.HashMap; 039import java.util.Locale; 040import java.util.Map; 041 042import javax.servlet.ServletRequest; 043import javax.servlet.jsp.JspException; 044import javax.servlet.jsp.tagext.TagSupport; 045 046import org.apache.commons.lang3.LocaleUtils; 047import org.apache.commons.logging.Log; 048 049/** 050 * Provides access to the properties of a resource in the OpenCms VFS .<p> 051 * 052 * Of particular importance is the setting of the <code>file</code> attribute, 053 * which can take the following values.<p> 054 * 055 * This attribute allows you to specify where to search for the property.<BR> 056 * The following values are supported: 057 * </P> 058 * <DL> 059 * <DT><b>uri</b> (default)</DT> 060 * <DD> Look up the property on the file with the 061 * uri requested by the user.</DD> 062 * <DT><b>search.uri</b> or <b>search</b></DT> 063 * <DD>Look up the property by also checking all parent folders for the property, 064 * starting with the file with uri requested by the user and 065 * going "upward" if the property was not found there.</DD> 066 * <DT><b>element.uri</b></DT> 067 * <DD>Look up the property on the currently 068 * processed sub - element. This is useful in templates or other pages that 069 * consist of many elements.</DD> 070 * <DT><b>search.element.uri</b></DT> 071 * <DD>Look up the property by also checking all parent folders for the 072 * property, starting with the file with the currently processed sub - 073 * element and going "upward" if the property was not found there.</DD> 074 * <DT>sitemap</DT> 075 * <DD>reads from the current sitemap entry</DD> 076 * <DT>search.sitemap</DT> 077 * <DD>Look up the property by also checking all parent sitemap entries 078 * for the property, starting with the current sitemap entry and 079 * going "upward" if the property was not found there.</DD> 080 * <DT>container</DT> 081 * <DD>reads from the current container element</DD> 082 * <DT><B>{some-file-uri}</B></DT> 083 * <DD>Look up the property on that exact file 084 * uri in the OpenCms VFS,<EM> fallback if no other valid option is 085 * selected for the file attribute.</EM></DD> 086 * </DL> 087 * 088 * <P>There are also some deprecated options for the "file" value that are 089 * still supported but should not longer be used:</P> 090 * <DL> 091 * <DT>parent</DT> 092 * <DD>same as <STRONG>uri</STRONG></DD> 093 * <DT>search-parent</DT> 094 * <DD>same as <STRONG>search.uri</STRONG></DD> 095 * <DT>this</DT> 096 * <DD>same as <STRONG>element.uri</STRONG></DD> 097 * <DT>search-this</DT> 098 * <DD>same as <STRONG>search.element.uri</STRONG></DD> 099 * </DL> 100 * 101 * @since 6.0.0 102 */ 103public class CmsJspTagProperty extends TagSupport { 104 105 /** Tells for which resource properties should be looked up (with or without searching), depending on the {@link FileUse}. */ 106 public static class CmsPropertyAction { 107 108 /** The VFS site path of the resource for which the properties should be read. */ 109 private String m_vfsUri; 110 /** A flag, indicating if the property should be searched or not. */ 111 private boolean m_search; 112 113 /** 114 * Default constructor. 115 * @param req the current servlet request. 116 * @param action the action to perform. 117 */ 118 public CmsPropertyAction(ServletRequest req, String action) { 119 120 CmsFlexController controller = CmsFlexController.getController(req); 121 122 FileUse useAction = FileUse.URI; 123 if (action != null) { 124 // if action is set overwrite default 125 useAction = FileUse.parse(action); 126 } 127 128 if (useAction != null) { 129 switch (useAction) { 130 case URI: 131 case PARENT: 132 // read properties of parent (i.e. top requested) file 133 m_vfsUri = controller.getCmsObject().getRequestContext().getUri(); 134 break; 135 case SEARCH: 136 case SEARCH_URI: 137 case SEARCH_PARENT: 138 // try to find property on parent file and all parent folders 139 m_vfsUri = controller.getCmsObject().getRequestContext().getUri(); 140 m_search = true; 141 break; 142 case ELEMENT_URI: 143 case THIS: 144 // read properties of this file 145 m_vfsUri = controller.getCurrentRequest().getElementUri(); 146 break; 147 case SEARCH_ELEMENT_URI: 148 case SEARCH_THIS: 149 // try to find property on this file and all parent folders 150 m_vfsUri = controller.getCurrentRequest().getElementUri(); 151 m_search = true; 152 break; 153 default: 154 // just to prevent the warning since all cases are handled 155 } 156 } else { 157 // read properties of the file named in the attribute 158 m_vfsUri = CmsLinkManager.getAbsoluteUri(action, controller.getCurrentRequest().getElementUri()); 159 m_search = false; 160 } 161 } 162 163 /** 164 * Returns the VFS site path of the resource for which the properties should be read. 165 * 166 * @return the VFS site path of the resource for which the properties should be read. 167 */ 168 public String getVfsUri() { 169 170 return m_vfsUri; 171 } 172 173 /** 174 * Returns <code>true</code> if it should be searched for the property, otherwise <code>false</code>. 175 * @return <code>true</code> if it should be searched for the property, otherwise <code>false</code>. 176 */ 177 public boolean isSearch() { 178 179 return m_search; 180 } 181 } 182 183 /** Constants for <code>file</code> attribute interpretation. */ 184 private enum FileUse { 185 186 /** Use element uri. */ 187 ELEMENT_URI("element.uri"), 188 /** Use parent (same as {@link #URI}). */ 189 PARENT("parent"), 190 /** Use search (same as {@link #SEARCH_URI}). */ 191 SEARCH("search"), 192 /** Use search element uri. */ 193 SEARCH_ELEMENT_URI("search.element.uri"), 194 /** Use search parent (same as {@link #SEARCH_URI}). */ 195 SEARCH_PARENT("search-parent"), 196 /** Use seach this (same as {@link #SEARCH_ELEMENT_URI}). */ 197 SEARCH_THIS("search-this"), 198 /** Use search uri. */ 199 SEARCH_URI("search.uri"), 200 /** Use sitemap entries. */ 201 /** Use this (same as {@link #ELEMENT_URI}). */ 202 THIS("this"), 203 /** Use uri. */ 204 URI("uri"); 205 206 /** Property name. */ 207 private String m_name; 208 209 /** Constructor.<p> 210 * @param name the string representation of the constant 211 **/ 212 private FileUse(String name) { 213 214 m_name = name; 215 } 216 217 /** 218 * Parses a string into an enumeration element.<p> 219 * 220 * @param name the name of the element 221 * 222 * @return the element with the given name or <code>null</code> if not found 223 */ 224 public static FileUse parse(String name) { 225 226 for (FileUse fileUse : FileUse.values()) { 227 if (fileUse.getName().equals(name)) { 228 return fileUse; 229 } 230 } 231 return null; 232 } 233 234 /** 235 * Returns the name.<p> 236 * 237 * @return the name 238 */ 239 public String getName() { 240 241 return m_name; 242 } 243 } 244 245 /** The log object for this class. */ 246 private static final Log LOG = CmsLog.getLog(CmsJspTagProperty.class); 247 248 /** Serial version UID required for safe serialization. */ 249 private static final long serialVersionUID = -4040833541258687977L; 250 251 /** The default value. */ 252 private String m_defaultValue; 253 254 /** The locale for which the property should be read. */ 255 private Locale m_locale; 256 257 /** Indicates if HTML should be escaped. */ 258 private boolean m_escapeHtml; 259 260 /** The file to read the property from. */ 261 private String m_propertyFile; 262 263 /** The name of the property to read. */ 264 private String m_propertyName; 265 266 /** 267 * Internal action method.<p> 268 * 269 * @param action the search action 270 * @param req the current request 271 * 272 * @return String the value of the property or <code>null</code> if not found (and no defaultValue provided) 273 * 274 * @throws CmsException if something goes wrong 275 */ 276 public static Map<String, String> propertiesTagAction(String action, ServletRequest req) throws CmsException { 277 278 CmsFlexController controller = CmsFlexController.getController(req); 279 280 // now read the property from the VFS 281 Map<String, String> value = new HashMap<String, String>(); 282 CmsPropertyAction propertyAction = new CmsPropertyAction(req, action); 283 if (null != propertyAction.getVfsUri()) { 284 value = CmsProperty.toMap( 285 controller.getCmsObject().readPropertyObjects(propertyAction.getVfsUri(), propertyAction.isSearch())); 286 } 287 return value; 288 } 289 290 /** 291 * Internal action method.<p> 292 * 293 * @param property the property to look up 294 * @param action the search action 295 * @param defaultValue the default value 296 * @param escape if the result html should be escaped or not 297 * @param req the current request 298 * 299 * @return the value of the property or <code>null</code> if not found (and no defaultValue was provided) 300 * 301 * @throws CmsException if something goes wrong 302 */ 303 public static String propertyTagAction( 304 String property, 305 String action, 306 String defaultValue, 307 boolean escape, 308 ServletRequest req) 309 throws CmsException { 310 311 return propertyTagAction(property, action, defaultValue, escape, req, null); 312 } 313 314 /** 315 * Internal action method.<p> 316 * 317 * @param property the property to look up 318 * @param action the search action 319 * @param defaultValue the default value 320 * @param escape if the result html should be escaped or not 321 * @param req the current request 322 * @param locale the locale for which the property should be read 323 * 324 * @return the value of the property or <code>null</code> if not found (and no defaultValue was provided) 325 * 326 * @throws CmsException if something goes wrong 327 */ 328 public static String propertyTagAction( 329 String property, 330 String action, 331 String defaultValue, 332 boolean escape, 333 ServletRequest req, 334 Locale locale) 335 throws CmsException { 336 337 CmsFlexController controller = CmsFlexController.getController(req); 338 String value = null; 339 CmsPropertyAction propertyAction = new CmsPropertyAction(req, action); 340 if (null != propertyAction.getVfsUri()) { 341 value = controller.getCmsObject().readPropertyObject( 342 propertyAction.getVfsUri(), 343 property, 344 propertyAction.isSearch(), 345 locale).getValue(); 346 } 347 if (value == null) { 348 value = defaultValue; 349 } 350 if (escape) { 351 // HTML escape the value 352 value = CmsEncoder.escapeHtml(value); 353 } 354 return value; 355 } 356 357 /** 358 * @see javax.servlet.jsp.tagext.TagSupport#doEndTag() 359 */ 360 @Override 361 public int doEndTag() { 362 363 release(); 364 return EVAL_PAGE; 365 } 366 367 /** 368 * @return SKIP_BODY 369 * @see javax.servlet.jsp.tagext.Tag#doStartTag() 370 */ 371 @Override 372 public int doStartTag() throws JspException { 373 374 ServletRequest req = pageContext.getRequest(); 375 376 // This will always be true if the page is called through OpenCms 377 if (CmsFlexController.isCmsRequest(req)) { 378 379 try { 380 String prop = propertyTagAction(getName(), getFile(), m_defaultValue, m_escapeHtml, req, m_locale); 381 // Make sure that no null String is returned 382 if (prop == null) { 383 prop = ""; 384 } 385 pageContext.getOut().print(prop); 386 387 } catch (Exception ex) { 388 if (LOG.isErrorEnabled()) { 389 LOG.error(Messages.get().getBundle().key(Messages.ERR_PROCESS_TAG_1, "property"), ex); 390 } 391 throw new javax.servlet.jsp.JspException(ex); 392 } 393 } 394 return SKIP_BODY; 395 } 396 397 /** 398 * Returns the default value.<p> 399 * 400 * @return the default value 401 */ 402 public String getDefault() { 403 404 return m_defaultValue != null ? m_defaultValue : ""; 405 } 406 407 /** 408 * The value of the escape html flag.<p> 409 * 410 * @return the value of the escape html flag 411 */ 412 public String getEscapeHtml() { 413 414 return "" + m_escapeHtml; 415 } 416 417 /** 418 * Returns the file name.<p> 419 * 420 * @return the file name 421 */ 422 public String getFile() { 423 424 return m_propertyFile != null ? m_propertyFile : "parent"; 425 } 426 427 /** 428 * Returns the property name.<p> 429 * 430 * @return String the property name 431 */ 432 public String getName() { 433 434 return m_propertyName != null ? m_propertyName : ""; 435 } 436 437 /** 438 * @see javax.servlet.jsp.tagext.Tag#release() 439 */ 440 @Override 441 public void release() { 442 443 super.release(); 444 m_propertyFile = null; 445 m_propertyName = null; 446 m_defaultValue = null; 447 m_escapeHtml = false; 448 } 449 450 /** 451 * Sets the default value.<p> 452 * 453 * This is used if a selected property is not found.<p> 454 * 455 * @param def the default value 456 */ 457 public void setDefault(String def) { 458 459 if (def != null) { 460 m_defaultValue = def; 461 } 462 } 463 464 /** 465 * Set the escape html flag.<p> 466 * 467 * @param value should be <code>"true"</code> or <code>"false"</code> (all values other then <code>"true"</code> are 468 * considered to be false) 469 */ 470 public void setEscapeHtml(String value) { 471 472 if (value != null) { 473 m_escapeHtml = Boolean.valueOf(value.trim()).booleanValue(); 474 } 475 } 476 477 /** 478 * Sets the file name.<p> 479 * 480 * @param file the file name 481 */ 482 public void setFile(String file) { 483 484 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(file)) { 485 m_propertyFile = file; 486 } 487 } 488 489 /** 490 * Sets the locale for which the property should be read. 491 * 492 * @param locale the locale for which the property should be read. 493 */ 494 public void setLocale(String locale) { 495 496 try { 497 m_locale = LocaleUtils.toLocale(locale); 498 } catch (IllegalArgumentException e) { 499 LOG.error(Messages.get().getBundle().key(Messages.ERR_TAG_INVALID_LOCALE_1, "cms:property"), e); 500 m_locale = null; 501 } 502 } 503 504 /** 505 * Sets the property name.<p> 506 * 507 * @param name the property name to set 508 */ 509 public void setName(String name) { 510 511 if (name != null) { 512 m_propertyName = name; 513 } 514 } 515 516}