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.CmsRequestContext; 032import org.opencms.flex.CmsFlexController; 033import org.opencms.jsp.parse.A_CmsConfiguredHtmlParser; 034import org.opencms.main.CmsException; 035import org.opencms.main.CmsLog; 036import org.opencms.util.CmsStringUtil; 037 038import java.io.PrintWriter; 039import java.io.StringWriter; 040import java.util.Iterator; 041import java.util.List; 042 043import javax.servlet.ServletRequest; 044import javax.servlet.jsp.JspException; 045import javax.servlet.jsp.PageContext; 046import javax.servlet.jsp.tagext.BodyTagSupport; 047 048import org.apache.commons.logging.Log; 049 050import org.htmlparser.util.ParserException; 051 052/** 053 * Implements the <code><cms:parse></cms:parse></code> tag to allow parsing of nested 054 * HTML with the {@link org.opencms.jsp.parse.A_CmsConfiguredHtmlParser}} implementation specified by the "parserClass" attribute. 055 * <p> 056 * 057 * @since 6.1.3 058 */ 059public class CmsJspTagParse extends BodyTagSupport { 060 061 /** 062 * The name of the mandatory Tag attribute for the visitor class an instance of will be guided 063 * throught the body content. 064 */ 065 public static final String ATT_VISITOR_CLASS = "parserClass"; 066 067 /** Tag name constant for log output. */ 068 public static final String TAG_NAME = "parse"; 069 070 /** The log object for this class. */ 071 private static final Log LOG = CmsLog.getLog(CmsJspTagParse.class); 072 073 /** Serial version UID required for safe serialization. */ 074 private static final long serialVersionUID = -6541745426202242240L; 075 076 /** Indicates the parse action should be disabled to allow inline editing in the container page editor. */ 077 private boolean m_allowInlineEdit; 078 079 /** The visitor / parser class name to use. */ 080 private String m_configuredParserClassname; 081 082 /** List of upper case tag name strings of tags that should not be auto-corrected if closing divs are missing. */ 083 private List<String> m_noAutoCloseTags; 084 085 /** The attribute value of the param attribute. */ 086 private String m_param = ""; 087 088 /** 089 * @see javax.servlet.jsp.tagext.Tag#doEndTag() 090 * 091 * @return EVAL_PAGE 092 * 093 * @throws JspException in case something goes wrong 094 */ 095 @Override 096 public int doEndTag() throws JspException { 097 098 ServletRequest req = pageContext.getRequest(); 099 A_CmsConfiguredHtmlParser parser; 100 if (m_allowInlineEdit && CmsJspTagEditable.isEditableRequest(req)) { 101 // during inline editing the content should not be parsed 102 try { 103 getBodyContent().writeOut(pageContext.getOut()); 104 release(); 105 } catch (Exception ex) { 106 release(); 107 if (LOG.isErrorEnabled()) { 108 LOG.error(Messages.get().getBundle().key(Messages.ERR_PROCESS_TAG_1, TAG_NAME), ex); 109 } 110 // this is severe 111 throw new JspException(ex); 112 } 113 } else { 114 // This will always be true if the page is called through OpenCms 115 if (CmsFlexController.isCmsRequest(req)) { 116 String content = ""; 117 try { 118 if (CmsStringUtil.isEmpty(m_configuredParserClassname)) { 119 if (LOG.isErrorEnabled()) { 120 LOG.error( 121 Messages.get().getBundle().key( 122 Messages.GUI_ERR_TAG_ATTRIBUTE_MISSING_2, 123 new Object[] {TAG_NAME, ATT_VISITOR_CLASS})); 124 } 125 126 } 127 // wrong attribute visitorClass -> content will remain empty, but no exception is 128 // thrown 129 try { 130 // load 131 Class<?> cl = Class.forName(m_configuredParserClassname); 132 // Instantiate 133 Object instance = cl.newInstance(); 134 // cast 135 parser = (A_CmsConfiguredHtmlParser)instance; 136 parser.setParam(m_param); 137 // cms object: 138 CmsFlexController controller = CmsFlexController.getController(req); 139 CmsObject cms = controller.getCmsObject(); 140 parser.setCmsObject(cms); 141 content = parseTagAction(getBodyContent().getString(), pageContext, parser); 142 143 } catch (Exception e) { 144 if (LOG.isErrorEnabled()) { 145 LOG.error( 146 Messages.get().getBundle().key( 147 Messages.GUI_ERR_TAG_ATTRIBUTE_INVALID_3, 148 new Object[] { 149 TAG_NAME, 150 ATT_VISITOR_CLASS, 151 A_CmsConfiguredHtmlParser.class.getName()}), 152 e); 153 } 154 e.printStackTrace(System.err); 155 } 156 157 } finally { 158 try { 159 getBodyContent().clear(); 160 getBodyContent().print(content); 161 getBodyContent().writeOut(pageContext.getOut()); 162 release(); 163 164 } catch (Exception ex) { 165 release(); 166 if (LOG.isErrorEnabled()) { 167 LOG.error(Messages.get().getBundle().key(Messages.ERR_PROCESS_TAG_1, TAG_NAME), ex); 168 } 169 // this is severe 170 throw new JspException(ex); 171 } 172 173 } 174 175 } 176 } 177 return EVAL_PAGE; 178 } 179 180 /** 181 * Getter for the attribute "noAutoCloseTags" of the <cms:parse> tag.<p> 182 * 183 * Returns a <code>String</code> that consists of the comma-separated upper case tag names for which this 184 * tag will not correct missing closing tags. <p> 185 * 186 * @return a String that consists of the comma-separated upper case tag names for which this 187 * tag will not correct missing closing tags. 188 */ 189 public String getNoAutoCloseTags() { 190 191 StringBuffer result = new StringBuffer(); 192 if ((m_noAutoCloseTags != null) && (m_noAutoCloseTags.size() > 0)) { 193 Iterator<String> it = m_noAutoCloseTags.iterator(); 194 while (it.hasNext()) { 195 result.append(it.next()).append(','); 196 } 197 } 198 return result.toString(); 199 } 200 201 /** 202 * Returns the param.<p> 203 * 204 * @return the param 205 */ 206 public String getParam() { 207 208 return m_param; 209 } 210 211 /** 212 * Returns the fully qualified class name of the {@link A_CmsConfiguredHtmlParser} class to use 213 * for parsing.<p> 214 * 215 * @return the parserrClass 216 */ 217 public String getParserClass() { 218 219 return m_configuredParserClassname; 220 } 221 222 /** 223 * Returns if the parse action should be disabled to allow inline editing in the container page editor.<p> 224 * 225 * @return <code>true</code> if the parse action should be disabled to allow inline editing in the container page editor 226 */ 227 public boolean isAllowInlineEdit() { 228 229 return m_allowInlineEdit; 230 } 231 232 /** 233 * Internal action method.<p> 234 * 235 * Parses (and potentially transforms) a HTMl content block.<p> 236 * 237 * @param content the content to be parsed / transformed 238 * @param context needed for getting the encoding / the locale 239 * @param parser the visitor / parser to use 240 * 241 * @return the transformed content 242 */ 243 public String parseTagAction(String content, PageContext context, A_CmsConfiguredHtmlParser parser) { 244 245 String result = null; 246 CmsRequestContext cmsContext = CmsFlexController.getCmsObject(context.getRequest()).getRequestContext(); 247 248 if (parser == null) { 249 if (LOG.isErrorEnabled()) { 250 LOG.error( 251 Messages.get().getBundle(cmsContext.getLocale()).key( 252 Messages.GUI_ERR_TAG_ATTRIBUTE_MISSING_2, 253 new Object[] {TAG_NAME, ATT_VISITOR_CLASS})); 254 } 255 result = content; 256 } else { 257 258 String encoding = cmsContext.getEncoding(); 259 try { 260 result = parser.doParse(content, encoding, m_noAutoCloseTags); 261 262 } catch (ParserException pex) { 263 264 if (LOG.isErrorEnabled()) { 265 LOG.error( 266 Messages.get().getBundle(cmsContext.getLocale()).key( 267 Messages.ERR_PROCESS_TAG_1, 268 new Object[] {TAG_NAME}), 269 pex); 270 } 271 StringWriter stackTrace = new StringWriter(); 272 PrintWriter writer = new PrintWriter(new StringWriter()); 273 StringBuffer msg = new StringBuffer("<!--\n").append(pex.getLocalizedMessage()).append("\n"); 274 pex.printStackTrace(writer); 275 msg.append(stackTrace.toString()).append("\n-->"); 276 result = msg.toString(); 277 } catch (CmsException cmex) { 278 if (LOG.isErrorEnabled()) { 279 LOG.error( 280 Messages.get().getBundle(cmsContext.getLocale()).key( 281 Messages.ERR_PROCESS_TAG_1, 282 new Object[] {TAG_NAME}), 283 cmex); 284 } 285 StringWriter stackTrace = new StringWriter(); 286 PrintWriter writer = new PrintWriter(new StringWriter()); 287 StringBuffer msg = new StringBuffer("<!--\n").append(cmex.getLocalizedMessage()).append("\n"); 288 cmex.printStackTrace(writer); 289 msg.append(stackTrace.toString()).append("\n-->"); 290 result = msg.toString(); 291 } 292 293 } 294 return result; 295 } 296 297 /** 298 * @see javax.servlet.jsp.tagext.Tag#release() 299 */ 300 @Override 301 public void release() { 302 303 m_configuredParserClassname = null; 304 m_param = null; 305 super.release(); 306 } 307 308 /** 309 * Sets if the parse action should be disabled to allow inline editing in the container page editor.<p> 310 * 311 * @param allowInlineEdit <code>true</code> to allow inline editing 312 */ 313 public void setAllowInlineEdit(boolean allowInlineEdit) { 314 315 m_allowInlineEdit = allowInlineEdit; 316 } 317 318 /** 319 * Setter for the attribute "noAutoCloseTags" of the <cms:parse> tag.<p> 320 * 321 * Awaits a <code>String</code> that consists of the comma-separated upper case tag names for which this 322 * tag should not correct missing closing tags.<p> 323 * 324 * @param noAutoCloseTagList a <code>String</code> that consists of the comma-separated upper case tag names for which this 325 * tag should not correct missing closing tags 326 */ 327 public void setNoAutoCloseTags(String noAutoCloseTagList) { 328 329 m_noAutoCloseTags = CmsStringUtil.splitAsList(noAutoCloseTagList, ','); 330 331 } 332 333 /** 334 * Sets the param.<p> 335 * 336 * @param param the param to set 337 */ 338 public void setParam(String param) { 339 340 m_param = param; 341 } 342 343 /** 344 * Sets the fully qualified class name of the {@link A_CmsConfiguredHtmlParser} class to use for 345 * parsing.<p> 346 * 347 * @param parserClass the fully qualified class name of the {@link A_CmsConfiguredHtmlParser} 348 * class to use for parsing 349 */ 350 public void setParserClass(String parserClass) { 351 352 m_configuredParserClassname = parserClass; 353 } 354}