diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/Bebop.java b/ccm-core/src/main/java/com/arsdigita/bebop/Bebop.java deleted file mode 100644 index af58f5e2f..000000000 --- a/ccm-core/src/main/java/com/arsdigita/bebop/Bebop.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -package com.arsdigita.bebop; - -import org.apache.log4j.Logger; - -/** - * @author Justin Ross - * @see com.arsdigita.bebop.BebopConfig - * @version $Id$ - */ -public final class Bebop { - - private static final Logger s_log = Logger.getLogger(Bebop.class); - - private static BebopConfig s_config = BebopConfig.getInstance(); - - /** - * Gets the BebopConfig object. - */ - public static BebopConfig getConfig() { - return s_config; - } -} diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/BebopConfig.java b/ccm-core/src/main/java/com/arsdigita/bebop/BebopConfig.java index f322fa2d4..46262db4c 100755 --- a/ccm-core/src/main/java/com/arsdigita/bebop/BebopConfig.java +++ b/ccm-core/src/main/java/com/arsdigita/bebop/BebopConfig.java @@ -1,191 +1,283 @@ /* - * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved. + * Copyright (C) 2016 LibreCCM Foundation. * * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA */ package com.arsdigita.bebop; import com.arsdigita.bebop.page.PageTransformer; import com.arsdigita.bebop.util.BebopConstants; -import com.arsdigita.runtime.AbstractConfig; import com.arsdigita.templating.PresentationManager; import com.arsdigita.ui.SimplePage; -import com.arsdigita.util.parameter.BooleanParameter; -import com.arsdigita.util.parameter.ClassParameter; -import com.arsdigita.util.parameter.EnumerationParameter; -import com.arsdigita.util.parameter.Parameter; -import com.arsdigita.util.parameter.SingletonParameter; -import com.arsdigita.util.parameter.StringParameter; -import org.apache.log4j.Logger; +import com.arsdigita.util.UncheckedWrapperException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.StringJoiner; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.configuration.Configuration; +import org.libreccm.configuration.ConfigurationManager; +import org.libreccm.configuration.Setting; /** - * @author Justin Ross - * @see com.arsdigita.bebop.Bebop - * @version $Id: BebopConfig.java 1498 2007-03-19 16:22:15Z apevec $ + * + * @author Jens Pelzetter */ -public final class BebopConfig extends AbstractConfig { - - /** A logger instance to assist debugging. */ - private static final Logger s_log = Logger.getLogger(BebopConfig.class); +@Configuration(descBundle = "com.arsdigita.bebop.BebopConfigDescription", + descKey = "bebop.config.description") +public final class BebopConfig { - /** Singleton config object. */ - private static BebopConfig s_config; - - /** - * Gain a BebopConfig object. - * - * Singleton pattern, don't instantiate a config object using the - * constructor directly! - * @return - */ - public static synchronized BebopConfig getInstance() { - if (s_config == null) { - s_config = new BebopConfig(); - s_config.load(); + @Setting(descKey = "bebop.config.presenter_class_name") + private String presenterClassName = PageTransformer.class.getName(); + + @Setting(descKey = "bebop.config.base_page_name") + private String basePageClassName = SimplePage.class.getName(); + + @Setting(descKey = "bebop.config.tidy_config_file") + private String tidyConfigFile + = "com/arsdigita/bebop/parameters/tidy.properties"; + + @Setting(descKey = "bebop.config.fancy_errors") + private Boolean fancyErrors = false; + + @Setting(descKey = "bebop.config.dcp_on_buttons") + private Boolean dcpOnButtons = true; + + @Setting(descKey = "bebop.config.dcp_on_links") + private Boolean dcpOnLinks = false; + + @Setting(descKey = "bebop.config.tree_select_enabled") + private Boolean treeSelectEnabled = false; + + @Setting(descKey = "bebop.config.dhtml_editors") + private Set dhtmlEditors = new HashSet<>( + Arrays.asList(new String[]{BebopConstants.BEBOP_XINHAEDITOR, + BebopConstants.BEBOP_FCKEDITOR, + BebopConstants.BEBOP_DHTMLEDITOR})); + + @Setting(descKey = "bebop.config.default_dhtml_editor") + private String defaultDhtmlEditor = BebopConstants.BEBOP_XINHAEDITOR; + + @Setting(descKey = "bebop.config.dhtml_editor_srcfile") + private String dhtmlEditorSrcFile = "/assets/xinha/XinhaLoader.js"; + + @Setting(descKey = "bebop.config.show_class_name") + private Boolean showClassName = false; + + public static BebopConfig getConfig() { + final CdiUtil cdiUtil = new CdiUtil(); + final ConfigurationManager confManager = cdiUtil.findBean( + ConfigurationManager.class); + return confManager.findConfiguration(BebopConfig.class); + } + + public String getPresenterClassName() { + return presenterClassName; + } + + public Class getPresenterClass() { + try { + return (Class) Class. + forName(presenterClassName); + } catch (ClassNotFoundException ex) { + throw new UncheckedWrapperException(ex); } - - return s_config; } - - // set of configuration parameters - // ///////////////////////////////////////////////////////////////// - - /** - * */ - private final Parameter m_presenter = new SingletonParameter - ("waf.bebop.presentation_manager", Parameter.REQUIRED, - new PageTransformer()); - /** - * - */ - private final Parameter m_page = new ClassParameter - ("waf.bebop.base_page", Parameter.REQUIRED, SimplePage.class); - /** Pointer to JTidy validation listener config file */ - private final Parameter m_tidy = new StringParameter - ("waf.bebop.tidy_config_file", Parameter.REQUIRED, - "com/arsdigita/bebop/parameters/tidy.properties"); - private final Parameter m_fancyErrors = new BooleanParameter - ("waf.bebop.fancy_xsl_errors", - Parameter.REQUIRED, - Boolean.FALSE); - /** Double Click Protection, enabled by default for all buttons in a form.*/ - private final Parameter m_dcpOnButtons = new BooleanParameter - ("waf.bebop.dcp_on_buttons", Parameter.REQUIRED, Boolean.TRUE); - /** Double Click Protection, disabled by default for all links. */ - private final Parameter m_dcpOnLinks = new BooleanParameter - ("waf.bebop.dcp_on_links", Parameter.REQUIRED, Boolean.FALSE); - /** - * - */ - private final Parameter m_enableTreeSelect = new BooleanParameter - ("waf.bebop.enable_tree_select_attribute", - Parameter.REQUIRED, - Boolean.FALSE); - /** List of supported DHTML editors, first one is default (Xinha) */ - private final EnumerationParameter m_dhtmlEditor; - /** Path to DHTML editor source file, relativ to document root */ - private final Parameter m_dhtmlEditorSrcFile; - /** */ - private final Parameter m_showClassName = new BooleanParameter - ("waf.bebop.show_class_name", Parameter.OPTIONAL, Boolean.FALSE); - - /** - * Constructor. - * Singelton pattern, don't instantiate a config object using the - * constructor directly! Use getConfig() instead. - * - */ - public BebopConfig() { - - /** List of supported DHTML editors, first one is default (Xinha) */ - m_dhtmlEditor = new EnumerationParameter("waf.bebop.dhtml_editor", - Parameter.REQUIRED,BebopConstants.BEBOP_XINHAEDITOR); - m_dhtmlEditor.put("Xinha", BebopConstants.BEBOP_XINHAEDITOR); - m_dhtmlEditor.put("FCKeditor", BebopConstants.BEBOP_FCKEDITOR); - // HTMLArea for backwards compatibility with old XSL. to be removed soon! - m_dhtmlEditor.put("HTMLArea", BebopConstants.BEBOP_DHTMLEDITOR); - - // Xinha is now default! - m_dhtmlEditorSrcFile = new StringParameter - ("waf.bebop.dhtml_editor_src", Parameter.REQUIRED, - "/assets/xinha/XinhaLoader.js"); - - register(m_presenter); - register(m_page); - register(m_tidy); - register(m_fancyErrors); - register(m_dhtmlEditor); - register(m_dhtmlEditorSrcFile); - register(m_dcpOnButtons); - register(m_dcpOnLinks); - register(m_enableTreeSelect); - register(m_showClassName); - - loadInfo(); + + public void setPresenterClassName(final String presenterClassName) { + this.presenterClassName = presenterClassName; } - - /** - * Gets the configured PresentationManger. - */ - public final PresentationManager getPresentationManager() { - return (PresentationManager) get(m_presenter); + + public void setPresenterClass( + final Class presenterClass) { + setPresenterClassName(presenterClass.getName()); } - - final Class getBasePageClass() { - return (Class) get(m_page); + + public String getBasePageClassName() { + return basePageClassName; } - - /** - * I don't *want* to make this public. XXX - */ - public final String getTidyConfigFile() { - return (String) get(m_tidy); + + public Class getBasePageClass() { + try { + return (Class)Class.forName(basePageClassName); + } catch (ClassNotFoundException ex) { + throw new UncheckedWrapperException(ex); + } } - - public boolean wantFancyXSLErrors() { - return ((Boolean)get(m_fancyErrors)).booleanValue(); + + public void setBasePageClassName(final String basePageClassName) { + this.basePageClassName = basePageClassName; } - - public final boolean doubleClickProtectionOnButtons() { - return ((Boolean) get(m_dcpOnButtons)).booleanValue(); + + public void setBasePageClass(final Class basePageClass) { + setBasePageClassName(basePageClass.getName()); } - - public final boolean doubleClickProtectionOnLinks() { - return ((Boolean) get(m_dcpOnLinks)).booleanValue(); + + public String getTidyConfigFile() { + return tidyConfigFile; } - - public final boolean treeSelectAttributeEnabled() { - return ((Boolean) get(m_enableTreeSelect)).booleanValue(); + + public void setTidyConfigFile(final String tidyConfigFile) { + this.tidyConfigFile = tidyConfigFile; } - - /** - * Gets the DHTML editor to use - */ - public final String getDHTMLEditor() { - return (String) get(m_dhtmlEditor); + + public Boolean getFancyErrors() { + return fancyErrors; } - - /** - * Gets the location of DHTML editor's source file - */ - public final String getDHTMLEditorSrcFile() { - return (String) get(m_dhtmlEditorSrcFile); + + public void setFancyErrors(final Boolean fancyErrors) { + this.fancyErrors = fancyErrors; } - - public final boolean showClassName() { - return ((Boolean) get(m_showClassName)).booleanValue(); + + public Boolean getDcpOnButtons() { + return dcpOnButtons; + } + + public void setDcpOnButtons(final Boolean dcpOnButtons) { + this.dcpOnButtons = dcpOnButtons; + } + + public Boolean getDcpOnLinks() { + return dcpOnLinks; + } + + public void setDcpOnLinks(final Boolean dcpOnLinks) { + this.dcpOnLinks = dcpOnLinks; + } + + public Boolean isTreeSelectEnabled() { + return treeSelectEnabled; + } + + public void setTreeSelectEnabled(final Boolean treeSelectEnabled) { + this.treeSelectEnabled = treeSelectEnabled; + } + + public Set getDhtmlEditors() { + return new HashSet<>(dhtmlEditors); + } + + public void setDhtmlEditors(final Set dhtmlEditors) { + this.dhtmlEditors = dhtmlEditors; + } + + public String getDefaultDhtmlEditor() { + return defaultDhtmlEditor; + } + + public void setDefaultDhtmlEditor(final String defaultDhtmlEditor) { + this.defaultDhtmlEditor = defaultDhtmlEditor; + } + + public String getDhtmlEditorSrcFile() { + return dhtmlEditorSrcFile; + } + + public void setDhtmlEditorSrcFile(final String dhtmlEditorSrcFile) { + this.dhtmlEditorSrcFile = dhtmlEditorSrcFile; + } + + public Boolean getShowClassName() { + return showClassName; + } + + public void setShowClassName(final Boolean showClassName) { + this.showClassName = showClassName; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 89 * hash + Objects.hashCode(tidyConfigFile); + hash = 89 * hash + Objects.hashCode(fancyErrors); + hash = 89 * hash + Objects.hashCode(dcpOnButtons); + hash = 89 * hash + Objects.hashCode(dcpOnLinks); + hash = 89 * hash + Objects.hashCode(treeSelectEnabled); + hash = 89 * hash + Objects.hashCode(dhtmlEditors); + hash = 89 * hash + Objects.hashCode(defaultDhtmlEditor); + hash = 89 * hash + Objects.hashCode(dhtmlEditorSrcFile); + hash = 89 * hash + Objects.hashCode(showClassName); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof BebopConfig)) { + return false; + } + final BebopConfig other = (BebopConfig) obj; + if (!Objects.equals(tidyConfigFile, other.getTidyConfigFile())) { + return false; + } + if (!Objects.equals(defaultDhtmlEditor, other.getDefaultDhtmlEditor())) { + return false; + } + if (!Objects.equals(dhtmlEditorSrcFile, other.getDhtmlEditorSrcFile())) { + return false; + } + if (!Objects.equals(fancyErrors, other.getFancyErrors())) { + return false; + } + if (!Objects.equals(dcpOnButtons, other.getDcpOnButtons())) { + return false; + } + if (!Objects.equals(dcpOnLinks, other.getDcpOnLinks())) { + return false; + } + if (!Objects.equals(treeSelectEnabled, other.isTreeSelectEnabled())) { + return false; + } + if (!Objects.equals(dhtmlEditors, other.getDhtmlEditors())) { + return false; + } + return Objects.equals(showClassName, other.getShowClassName()); + } + + @Override + public String toString() { + final StringJoiner joiner = new StringJoiner(", "); + dhtmlEditors.forEach(s -> joiner.add(s)); + + return String.format("%s{ " + + "tidyConfigFile = %s, " + + "fancyErrors = %b, " + + "dcpOnButtons = %b, " + + "dcpOnLinks = %b, " + + "treeSelectEnabled = %b, " + + "dhtmlEditors = { %s }, " + + "defaultDhtmlEditor = %s, " + + "dhtmlEditorSrcFile = %s, " + + "showClassName = %b" + + " }", + super.toString(), + tidyConfigFile, + fancyErrors, + dcpOnButtons, + dcpOnLinks, + treeSelectEnabled, + joiner.toString(), + defaultDhtmlEditor, + dhtmlEditorSrcFile, + showClassName); } } diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/PageFactory.java b/ccm-core/src/main/java/com/arsdigita/bebop/PageFactory.java index 0f9f5a700..4c148f78d 100644 --- a/ccm-core/src/main/java/com/arsdigita/bebop/PageFactory.java +++ b/ccm-core/src/main/java/com/arsdigita/bebop/PageFactory.java @@ -170,7 +170,7 @@ public class PageFactory { if (initialized) { return; } - setPageClass(Bebop.getConfig().getBasePageClass()); + setPageClass(BebopConfig.getConfig().getBasePageClass()); } /** diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/SimpleComponent.java b/ccm-core/src/main/java/com/arsdigita/bebop/SimpleComponent.java index 06eef74e3..794b5c8bd 100755 --- a/ccm-core/src/main/java/com/arsdigita/bebop/SimpleComponent.java +++ b/ccm-core/src/main/java/com/arsdigita/bebop/SimpleComponent.java @@ -295,7 +295,7 @@ public class SimpleComponent extends Completable m_attr.exportAttributes(target); } if (KernelConfig.getConfig().isDebugEnabled() || - Bebop.getConfig().showClassName()) { + BebopConfig.getConfig().getShowClassName()) { target.addAttribute("bebop:classname", getClass().getName(), BEBOP_XML_NS); } diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/Tree.java b/ccm-core/src/main/java/com/arsdigita/bebop/Tree.java index d335bb90d..5489fa252 100644 --- a/ccm-core/src/main/java/com/arsdigita/bebop/Tree.java +++ b/ccm-core/src/main/java/com/arsdigita/bebop/Tree.java @@ -62,7 +62,7 @@ public class Tree extends SimpleComponent implements Resettable { Logger.getLogger(Tree.class); private static final boolean s_selectAttributeEnabled = - Bebop.getConfig().treeSelectAttributeEnabled(); + BebopConfig.getConfig().isTreeSelectEnabled(); // Any node id in the currentState is equivalent // to that node being expanded. If node id is diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/form/DHTMLEditor.java b/ccm-core/src/main/java/com/arsdigita/bebop/form/DHTMLEditor.java index 84bbe2eb5..a77a4ad0a 100755 --- a/ccm-core/src/main/java/com/arsdigita/bebop/form/DHTMLEditor.java +++ b/ccm-core/src/main/java/com/arsdigita/bebop/form/DHTMLEditor.java @@ -22,7 +22,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; -import com.arsdigita.bebop.Bebop; +import com.arsdigita.bebop.BebopConfig; import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.parameters.ParameterModel; import com.arsdigita.bebop.parameters.StringParameter; @@ -182,12 +182,13 @@ public class DHTMLEditor extends TextArea { } public String getEditorURL() { - return Bebop.getConfig().getDHTMLEditorSrcFile().substring( - 0, Bebop.getConfig().getDHTMLEditorSrcFile().lastIndexOf("/") + 1); + return BebopConfig.getConfig().getDhtmlEditorSrcFile().substring( + 0, + BebopConfig.getConfig().getDhtmlEditorSrcFile().lastIndexOf("/") + 1); } public String getEditorSrc() { - return Bebop.getConfig().getDHTMLEditorSrcFile(); + return BebopConfig.getConfig().getDhtmlEditorSrcFile(); } /** @@ -264,7 +265,7 @@ public class DHTMLEditor extends TextArea { */ @Override protected String getElementTag() { - return Bebop.getConfig().getDHTMLEditor(); + return BebopConfig.getConfig().getDefaultDhtmlEditor(); } /** diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/page/PageTransformer.java b/ccm-core/src/main/java/com/arsdigita/bebop/page/PageTransformer.java index 0033a7fd8..df46062e6 100755 --- a/ccm-core/src/main/java/com/arsdigita/bebop/page/PageTransformer.java +++ b/ccm-core/src/main/java/com/arsdigita/bebop/page/PageTransformer.java @@ -18,7 +18,7 @@ */ package com.arsdigita.bebop.page; -import com.arsdigita.bebop.Bebop; +import com.arsdigita.bebop.BebopConfig; import com.arsdigita.dispatcher.DispatcherHelper; import com.arsdigita.globalization.Globalization; import com.arsdigita.globalization.GlobalizationHelper; @@ -84,199 +84,199 @@ public class PageTransformer implements PresentationManager { registerXSLParameterGenerator("contextPath", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - return request - .getContextPath(); - } + @Override + public String generateValue( + HttpServletRequest request) { + return request + .getContextPath(); + } - }); + }); registerXSLParameterGenerator("root-context-prefix", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - return Web.getConfig() - .getDispatcherContextPath(); - } + @Override + public String generateValue( + HttpServletRequest request) { + return Web.getConfig() + .getDispatcherContextPath(); + } - }); + }); registerXSLParameterGenerator("context-prefix", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - return Web.getWebContext() - .getRequestURL() - .getContextPath(); - } + @Override + public String generateValue( + HttpServletRequest request) { + return Web.getWebContext() + .getRequestURL() + .getContextPath(); + } - }); + }); registerXSLParameterGenerator("internal-theme", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - return Web.getWebContext() - .getRequestURL() - .getContextPath() - + com.arsdigita.web.URL.INTERNAL_THEME_DIR; - } + @Override + public String generateValue( + HttpServletRequest request) { + return Web.getWebContext() + .getRequestURL() + .getContextPath() + + com.arsdigita.web.URL.INTERNAL_THEME_DIR; + } - }); + }); registerXSLParameterGenerator("dispatcher-prefix", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - return com.arsdigita.web.URL - .getDispatcherPath(); - } + @Override + public String generateValue( + HttpServletRequest request) { + return com.arsdigita.web.URL + .getDispatcherPath(); + } - }); + }); registerXSLParameterGenerator("dcp-on-buttons", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - if (Bebop.getConfig() - .doubleClickProtectionOnButtons()) { - return "true"; - } else { - return null; - } + @Override + public String generateValue( + HttpServletRequest request) { + if (BebopConfig.getConfig() + .getDcpOnButtons()) { + return "true"; + } else { + return null; + } - } + } - }); + }); registerXSLParameterGenerator("dcp-on-links", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - if (Bebop.getConfig() - .doubleClickProtectionOnLinks()) { - return "true"; - } else { - return null; - } + @Override + public String generateValue( + HttpServletRequest request) { + if (BebopConfig.getConfig() + .getDcpOnLinks()) { + return "true"; + } else { + return null; + } - } + } - }); + }); registerXSLParameterGenerator("user-agent", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - return request.getHeader( - "User-Agent"); - } + @Override + public String generateValue( + HttpServletRequest request) { + return request.getHeader( + "User-Agent"); + } - }); + }); registerXSLParameterGenerator("negotiated-language", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - return GlobalizationHelper - .getNegotiatedLocale() - .getLanguage(); - } + @Override + public String generateValue( + HttpServletRequest request) { + return GlobalizationHelper + .getNegotiatedLocale() + .getLanguage(); + } - }); + }); registerXSLParameterGenerator("selected-language", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - Locale selectedLocale - = com.arsdigita.globalization.GlobalizationHelper - .getSelectedLocale(request); - return (selectedLocale != null) - ? selectedLocale - .toString() : ""; - } + @Override + public String generateValue( + HttpServletRequest request) { + Locale selectedLocale + = com.arsdigita.globalization.GlobalizationHelper. + getSelectedLocale(request); + return (selectedLocale != null) + ? selectedLocale + .toString() : ""; + } - }); + }); registerXSLParameterGenerator("request-scheme", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - return request.getScheme(); - } + @Override + public String generateValue( + HttpServletRequest request) { + return request.getScheme(); + } - }); + }); registerXSLParameterGenerator("server-name", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - return request.getServerName(); - } + @Override + public String generateValue( + HttpServletRequest request) { + return request.getServerName(); + } - }); + }); registerXSLParameterGenerator("server-port", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - return Integer.toString( - request.getServerPort()); - } + @Override + public String generateValue( + HttpServletRequest request) { + return Integer.toString( + request.getServerPort()); + } - }); + }); registerXSLParameterGenerator("host", new XSLParameterGenerator() { - @Override - public String generateValue( - HttpServletRequest request) { - if (request.getServerPort() - == 80) { - return String.format( - "%s://%s", request - .getScheme(), request - .getServerName()); - } else { - return String.format( - "%s://%s:%d", - request.getScheme(), - request - .getServerName(), - request - .getServerPort()); - } - } + @Override + public String generateValue( + HttpServletRequest request) { + if (request.getServerPort() + == 80) { + return String.format( + "%s://%s", request + .getScheme(), request + .getServerName()); + } else { + return String.format( + "%s://%s:%d", + request.getScheme(), + request + .getServerName(), + request + .getServerPort()); + } + } - }); + }); s_log.debug("Static initalizer finished."); } @@ -343,7 +343,7 @@ public class PageTransformer implements PresentationManager { try { return new PrintWriter(new OutputStreamWriter(resp. - getOutputStream(), + getOutputStream(), charset)); } catch (IOException ex) { throw new UncheckedWrapperException(ex); @@ -372,11 +372,11 @@ public class PageTransformer implements PresentationManager { * always be passed to XSLT, which is the value of * req.getWebContextPath(). * - * @param doc the Bebop page to serve - * @param req the servlet request - * @param resp the servlet response + * @param doc the Bebop page to serve + * @param req the servlet request + * @param resp the servlet response * @param params a set of name-value pairs to pass as parameters to the - * Transformer + * Transformer */ public void servePage(final Document doc, final HttpServletRequest req, @@ -393,24 +393,24 @@ public class PageTransformer implements PresentationManager { try { final String charset = Globalization - .getDefaultCharset(DispatcherHelper.getNegotiatedLocale()); + .getDefaultCharset(DispatcherHelper.getNegotiatedLocale()); final String output = req.getParameter("output"); s_log.info("output=" + output); if (output == null) { - boolean fancyErrors = Bebop.getConfig().wantFancyXSLErrors() - || Boolean.TRUE.equals(req - .getAttribute( - FANCY_ERRORS)); + boolean fancyErrors + = BebopConfig.getConfig().getFancyErrors() + || Boolean.TRUE.equals(req.getAttribute( + FANCY_ERRORS)); // Get the stylesheet transformer object corresponding to the // current request. final XSLTemplate template = Templating.getTemplate( - req, - fancyErrors, - !Boolean.TRUE.equals(req.getAttribute(CACHE_XSL_NONE))); + req, + fancyErrors, + !Boolean.TRUE.equals(req.getAttribute(CACHE_XSL_NONE))); final PrintWriter writer = getWriter(resp, "text/html", charset); @@ -450,15 +450,15 @@ public class PageTransformer implements PresentationManager { new StreamResult(writer)); } catch (TransformerException ex) { throw new UncheckedWrapperException( - "cannot transform document", ex); + "cannot transform document", ex); } // copy and paste from BasePresentationManager if (KernelConfig.getConfig().isDebugEnabled()) { Document origDoc = (Document) req.getAttribute( - "com.arsdigita.xml.Document"); + "com.arsdigita.xml.Document"); Debugger.addDebugger(new TransformationDebugger(template. - getSource(), template.getDependents())); + getSource(), template.getDependents())); writer.print(Debugger.getDebugging(req)); } @@ -478,11 +478,11 @@ public class PageTransformer implements PresentationManager { // current request. template = Templating.getTemplate(req, Boolean.TRUE.equals(req - .getAttribute( - PageTransformer.FANCY_ERRORS)), + .getAttribute( + PageTransformer.FANCY_ERRORS)), !Boolean.TRUE.equals(req - .getAttribute( - PageTransformer.CACHE_XSL_NONE))); + .getAttribute( + PageTransformer.CACHE_XSL_NONE))); endTransaction(req); } finally { } @@ -490,7 +490,7 @@ public class PageTransformer implements PresentationManager { try { Date now = new Date(); SimpleDateFormat fmt = new SimpleDateFormat( - "yyyy-MM-dd-HH-mm"); + "yyyy-MM-dd-HH-mm"); String prefix = "waf-xsl-" + fmt.format(now); final OutputStream os = resp.getOutputStream(); @@ -509,7 +509,7 @@ public class PageTransformer implements PresentationManager { } } else { throw new IllegalStateException(output - + " is an unknown output"); + + " is an unknown output"); } } finally { } @@ -524,9 +524,9 @@ public class PageTransformer implements PresentationManager { private void endTransaction(final HttpServletRequest req) { // There is no longer any need for a database handle. if (req.getAttribute(PageContext.EXCEPTION) == null) { - + } else { - + } } @@ -577,7 +577,7 @@ public class PageTransformer implements PresentationManager { HttpServletRequest request) { XSLParameterGenerator generator = (XSLParameterGenerator) s_XSLParameters - .get(name); + .get(name); if (generator != null) { return generator.generateValue(request); } else { @@ -600,7 +600,7 @@ public class PageTransformer implements PresentationManager { final Map.Entry entry = (Map.Entry) entries.next(); String value = ((XSLParameterGenerator) entry.getValue()). - generateValue(request); + generateValue(request); if (value == null) { // XSL does not like nulls value = ""; diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/parameters/TidyHTMLValidationListener.java b/ccm-core/src/main/java/com/arsdigita/bebop/parameters/TidyHTMLValidationListener.java index af5542d72..3cb4bb950 100755 --- a/ccm-core/src/main/java/com/arsdigita/bebop/parameters/TidyHTMLValidationListener.java +++ b/ccm-core/src/main/java/com/arsdigita/bebop/parameters/TidyHTMLValidationListener.java @@ -18,7 +18,7 @@ */ package com.arsdigita.bebop.parameters; -import com.arsdigita.bebop.Bebop; +import com.arsdigita.bebop.BebopConfig; import com.arsdigita.bebop.event.ParameterEvent; import com.arsdigita.bebop.event.ParameterListener; import com.arsdigita.util.StringUtils; @@ -102,7 +102,7 @@ public class TidyHTMLValidationListener implements ParameterListener { private static Properties getJTidyProperties() { if (s_tidyProperties == null) { s_tidyProperties = new LockableProperties(); - String path = Bebop.getConfig().getTidyConfigFile(); + String path = BebopConfig.getConfig().getTidyConfigFile(); InputStream is = TidyHTMLValidationListener.class.getClassLoader ().getResourceAsStream(path); diff --git a/ccm-core/src/main/java/com/arsdigita/templating/Templating.java b/ccm-core/src/main/java/com/arsdigita/templating/Templating.java index 17ee373e2..3e2d3389d 100755 --- a/ccm-core/src/main/java/com/arsdigita/templating/Templating.java +++ b/ccm-core/src/main/java/com/arsdigita/templating/Templating.java @@ -17,7 +17,7 @@ */ package com.arsdigita.templating; -import com.arsdigita.bebop.Bebop; +import com.arsdigita.bebop.BebopConfig; import com.arsdigita.kernel.KernelConfig; import com.arsdigita.util.Assert; import com.arsdigita.util.ExceptionUnwrapper; @@ -49,8 +49,8 @@ import org.apache.log4j.Logger; * An entry-point class for the functions of the templating package. The class * manages access to all theme files (XSL as well as css, pirctures, etc). * - * This class maintains a cache of XSLTemplate objects, managed - * via the getTemplate and purgeTemplate methods. + * This class maintains a cache of XSLTemplate objects, managed via + * the getTemplate and purgeTemplate methods. * * @author Dan Berrange * @author Justin Ross <jross@redhat.com> @@ -58,21 +58,26 @@ import org.apache.log4j.Logger; */ public class Templating { - /** Internal logger instance to faciliate debugging. Enable logging output - * by editing /WEB-INF/conf/log4j.properties int hte runtime environment - * and set com.arsdigita.templating.Templating=DEBUG by uncommenting it - * or adding the line. */ + /** + * Internal logger instance to faciliate debugging. Enable logging output by + * editing /WEB-INF/conf/log4j.properties int hte runtime environment and + * set com.arsdigita.templating.Templating=DEBUG by uncommenting it or + * adding the line. + */ private static final Logger s_log = Logger.getLogger(Templating.class); - /** This is the name of the attribute that is set in the request whose - * value, if present, is a collection of TransformerExceptions that - * can be used to produce a "pretty" error. */ + /** + * This is the name of the attribute that is set in the request whose value, + * if present, is a collection of TransformerExceptions that can be used to + * produce a "pretty" error. + */ public static final String FANCY_ERROR_COLLECTION = "fancyErrorCollection"; - - /** Config object containing various parameter */ + /** + * Config object containing various parameter + */ private static final TemplatingConfig s_config = TemplatingConfig - .getInstanceOf(); + .getInstanceOf(); static { s_log.debug("Static initalizer starting..."); @@ -81,15 +86,14 @@ public class Templating { TransformerException.class, new ExceptionUnwrapper() { - @Override - public Throwable unwrap(Throwable t) { - TransformerException ex = (TransformerException) t; - return ex.getCause(); - } - }); + @Override + public Throwable unwrap(Throwable t) { + TransformerException ex = (TransformerException) t; + return ex.getCause(); + } + }); // now we initiate the CacheTable here - // default cache size used to be 50, which is too high I reckon, // each template can eat up to 4 megs Integer setting = s_config.getCacheSize(); @@ -112,17 +116,16 @@ public class Templating { /** * Returns a new instance of the current presentation manager class. This is - * an object which has the - * {@link com.arsdigita.templating.PresentationManager PresentationManager} - * interface which can be used to transform an XML document into an output + * an object which has the + * {@link com.arsdigita.templating.PresentationManager PresentationManager} + * interface which can be used to transform an XML document into an output * stream. * * As of version 6.6.0 the bebop framework is the only instance to provide * an implementation. To avoid class hierachie kludge we directly return the * bebop config here. * - * @return an instance of the PresentationManager - * interface + * @return an instance of the PresentationManager interface */ /* NON Javadoc * Used to be deprecated up to version 6.6.0. Reverted to non-deprecated. @@ -134,17 +137,22 @@ public class Templating { * instead. */ public static PresentationManager getPresentationManager() { - return Bebop.getConfig().getPresentationManager(); + try { + return (PresentationManager) BebopConfig.getConfig(). + getPresenterClass().newInstance(); + } catch (IllegalAccessException | InstantiationException ex) { + throw new UncheckedWrapperException(ex); + } } /** * Retrieves an XSL template. If the template is already loaded in the - * cache, it will be returned. If the template has been modified since - * it was first generated, it will be regenerated first. + * cache, it will be returned. If the template has been modified since it + * was first generated, it will be regenerated first. * - * @param source the URL to the top-level template resource + * @param source the URL to the top-level template resource * @return an XSLTemplate instance representing - * source + * source */ public static synchronized XSLTemplate getTemplate(final URL source) { return getTemplate(source, false, true); @@ -152,19 +160,18 @@ public class Templating { /** * Retrieves an XSL template. If the template is already loaded in the - * cache, it will be returned. If the template has been modified since - * it was first generated, it will be regenerated first. + * cache, it will be returned. If the template has been modified since it + * was first generated, it will be regenerated first. * - * @param source the URL to the top-level template resource - * @param fancyErrors Should this place any xsl errors in the request - * for use by another class. If this is true, the - * the errors are stored for later use. - * @param useCache Should the templates be pulled from cache, if available? - * True means they are pulled from cache. False means - * they are pulled from the disk. If this is false - * the pages are also not placed in the cache. + * @param source the URL to the top-level template resource + * @param fancyErrors Should this place any xsl errors in the request for + * use by another class. If this is true, the the errors are stored for + * later use. + * @param useCache Should the templates be pulled from cache, if available? + * True means they are pulled from cache. False means they are pulled from + * the disk. If this is false the pages are also not placed in the cache. * @return an XSLTemplate instance representing - * source + * source */ public static synchronized XSLTemplate getTemplate(final URL source, boolean fancyErrors, @@ -177,11 +184,11 @@ public class Templating { Assert.exists(source, URL.class); XSLTemplate template = null; - + if (template == null) { if (s_log.isInfoEnabled()) { s_log.info("The template for URL " + source + " is not " - + "cached; creating and caching it now"); + + "cached; creating and caching it now"); } if (fancyErrors) { @@ -194,14 +201,14 @@ public class Templating { } } else if (KernelConfig.getConfig().isDebugEnabled() - && template.isModified()) { + && template.isModified()) { // XXX referencing Kernel above is a broken dependency. // Debug mode should be captured at a lower level, // probably on UtilConfig. if (s_log.isInfoEnabled()) { s_log.info("Template " + template + " has been modified; " - + "recreating it from scratch"); + + "recreating it from scratch"); } if (fancyErrors) { @@ -220,7 +227,7 @@ public class Templating { /** * Resolves and retrieves the template for the given request. * - * @param sreq The current request object + * @param sreq The current request object * @return The resolved XSLTemplate instance */ public static XSLTemplate getTemplate(final HttpServletRequest sreq) { @@ -231,13 +238,12 @@ public class Templating { * Resolves the template for the given request to an URL. * * @param sreq The current request object - * @param fancyErrors Should this place any xsl errors in the request - * for use by another class. If this is true, the - * the errors are stored for later use. + * @param fancyErrors Should this place any xsl errors in the request for + * use by another class. If this is true, the the errors are stored for + * later use. * @param useCache Should the templates be pulled from cache, if available? - * True means they are pulled from cache. False means - * they are pulled from the disk. If this is false - * the pages are also not placed in the cache. + * True means they are pulled from cache. False means they are pulled from + * the disk. If this is false the pages are also not placed in the cache. * @return The resolved XSLTemplate instance */ public static XSLTemplate getTemplate(final HttpServletRequest sreq, @@ -253,12 +259,10 @@ public class Templating { } /** - * Removes an XSL template from the internal cache. The template - * for source will be regenerated on the next request - * for it. + * Removes an XSL template from the internal cache. The template for + * source will be regenerated on the next request for it. * - * @param source the URL to the top-level template - * resource + * @param source the URL to the top-level template resource */ public static synchronized void purgeTemplate(final URL source) { if (s_log.isDebugEnabled()) { @@ -269,8 +273,8 @@ public class Templating { } /** - * Removes all cached template objects. All template objects will - * be regenerated on-demand as each gets requested. + * Removes all cached template objects. All template objects will be + * regenerated on-demand as each gets requested. */ public static synchronized void purgeTemplates() { if (s_log.isDebugEnabled()) { @@ -318,14 +322,14 @@ public class Templating { } /** - * Transforms an URL, that refers to a local resource inside the running - * CCM web application. NON-local URLs remain unmodified. - * - * In case of a virtual path "/resource" it is short-circuiting access to - * the resource servlet. All other http:// URLs are transformed into file:// - * for XSLT validation to work for these resources. It has the added benefit + * Transforms an URL, that refers to a local resource inside the running CCM + * web application. NON-local URLs remain unmodified. + * + * In case of a virtual path "/resource" it is short-circuiting access to + * the resource servlet. All other http:// URLs are transformed into file:// + * for XSLT validation to work for these resources. It has the added benefit * of speeding up loading of XSL... - * + * * Currently the direct file access method is perferred! As soon as we * refactor to directly access the published XSL files in the database and * to avoid the unnecessary intermediate step via the file system, we have @@ -335,19 +339,21 @@ public class Templating { static URL transformURL(URL url) { HttpHost self = Web.getConfig().getHost(); - /** Indicates whether url refers to a local resource inside the - * running CCM web application (inside it's webapp context) */ + /** + * Indicates whether url refers to a local resource inside the running + * CCM web application (inside it's webapp context) + */ Boolean isLocal = false; - /** Contains the transformed "localized" path to url, i.e. without - * host part. */ + /** + * Contains the transformed "localized" path to url, i.e. without host + * part. + */ String localPath = ""; - + // Check if the url refers to our own host - if (self.getName().equals(url.getHost()) - && ( (self.getPort() == url.getPort()) - || (url.getPort()== -1 && self.getPort()== 80) - ) - ) { + if (self.getName().equals(url.getHost()) + && ((self.getPort() == url.getPort()) + || (url.getPort() == -1 && self.getPort() == 80))) { // host part denotes to a local resource, cut off host part. localPath = url.getPath(); isLocal = true; @@ -365,17 +371,16 @@ public class Templating { s_log.debug("Installation context is >" + installContext + "<."); } if (!installContext.equals("")) { - // CCM is installed into a non-ROOT context - if (localPath.startsWith(installContext)) { - // remove webapp context part - localPath = localPath.substring(installContext.length()); - if (s_log.isDebugEnabled()) { - s_log.debug("WebApp context removed: >>" + - localPath + "<<"); - } + // CCM is installed into a non-ROOT context + if (localPath.startsWith(installContext)) { + // remove webapp context part + localPath = localPath.substring(installContext.length()); + if (s_log.isDebugEnabled()) { + s_log.debug("WebApp context removed: >>" + localPath + "<<"); } + } } - + if (isLocal) { // url specifies the running CCM host and port (or port isn't // specified in url and default for running CCM host @@ -385,52 +390,50 @@ public class Templating { localPath = localPath.substring("/resource".length()); //remove virtual part URL newURL = Web.findResource(localPath); //without host part here! if (s_log.isDebugEnabled()) { - s_log.debug("Transforming resource " + url + " to " + newURL); + s_log. + debug("Transforming resource " + url + " to " + + newURL); } return newURL; } else { // A real path to disk final String filename = Web.getServletContext() - .getRealPath(localPath); + .getRealPath(localPath); File file = new File(filename); if (file.exists()) { try { URL newURL = file.toURL(); if (s_log.isDebugEnabled()) { s_log.debug("Transforming resource " + url + " to " - + newURL); + + newURL); } return newURL; } catch (MalformedURLException ex) { throw new UncheckedWrapperException(ex); } - } else { - if (s_log.isDebugEnabled()) { - s_log.debug("File " + filename - + " doesn't exist on disk"); - } + } else if (s_log.isDebugEnabled()) { + s_log.debug("File " + filename + + " doesn't exist on disk"); } } - } else { - // url is not the (local) running CCM host, no transformation - // is done - if (s_log.isDebugEnabled()) { + } else // url is not the (local) running CCM host, no transformation + // is done + if (s_log.isDebugEnabled()) { s_log.debug("URL " + url + " is not local"); } - } return url; // returns the original, unmodified url here } } /** - * + * * @author pb */ class LoggingErrorListener implements ErrorListener { - private static final Logger s_log = - Logger.getLogger(LoggingErrorListener.class); + private static final Logger s_log + = Logger.getLogger(LoggingErrorListener.class); private final ArrayList m_errors; LoggingErrorListener() { @@ -458,7 +461,8 @@ class LoggingErrorListener implements ErrorListener { private void log(Level level, TransformerException ex) { s_log.log(level, "Transformer " + level + ": " - + ex.getLocationAsString() + ": " + ex.getMessage(), + + ex.getLocationAsString() + ": " + ex. + getMessage(), ex); m_errors.add(ex); } diff --git a/ccm-core/src/main/resources/com/arsdigita/bebop/BebopConfigDescription.properties b/ccm-core/src/main/resources/com/arsdigita/bebop/BebopConfigDescription.properties new file mode 100644 index 000000000..d09c2d7cc --- /dev/null +++ b/ccm-core/src/main/resources/com/arsdigita/bebop/BebopConfigDescription.properties @@ -0,0 +1,30 @@ +# Copyright (C) 2016 LibreCCM Foundation. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA + +bebop.config.description = Several configuration options for Bebop + +bebop.config.presenter_class_name = Determines what implementation of PresentationManager is used to style Bebop pages +bebop.config.base_page_name = Determines what implementation of BasePage is used to serve Bebop pages +bebop.config.tidy_config_file = Points to the configuration file for the JTidy parameter validation listener +bebop.config.fancy_errors = Always display fancy XSL error pages +bebop.config.dcp_on_buttons = Specify whether to apply javascript double click protection to form submits +bebop.config.dcp_on_links = Specify whether to apply javascript double click protection to anchor elements +bebop.config.tree_select_enabled = Specify whether to turn on isSelected XML attribute on bebop.Tree nodes (performance impact) +bebop.config.dhtml_editors = Lists of all available DHTML text input widgets +bebop.config.default_dhtml_editor = Select which DHTML text input widget is used for DHTMLEditor Bebop component. +bebop.config.dhtml_editor_srcfile = Relative path to the source javascript file +bebop.config.show_class_name = \ No newline at end of file diff --git a/ccm-core/src/main/resources/com/arsdigita/bebop/BebopConfig_parameter.properties b/ccm-core/src/main/resources/com/arsdigita/bebop/BebopConfig_parameter.properties deleted file mode 100755 index f42f02df5..000000000 --- a/ccm-core/src/main/resources/com/arsdigita/bebop/BebopConfig_parameter.properties +++ /dev/null @@ -1,44 +0,0 @@ -waf.bebop.base_page.title=Bebop base page class -waf.bebop.base_page.purpose=Determines what implementation of BasePage is used to serve Bebop pages -waf.bebop.base_page.example=com.arsdigita.ui.SimplePage -waf.bebop.base_page.format=[classname] - -waf.bebop.presentation_manager.title=Bebop presentation manager class -waf.bebop.presentation_manager.purpose=Determines what implementation of PresentationManager is used to style Bebop pages -waf.bebop.presentation_manager.example=com.arsdigita.bebop.page.PageTransformer -waf.bebop.presentation_manager.format=[classname] - -waf.bebop.tidy_config_file.title=JTidy validation listener config file -waf.bebop.tidy_config_file.purpose=Points to the configuration file for the JTidy parameter validation listener -waf.bebop.tidy_config_file.example=com/arsdigita/bebop/parameters/tidy.properties -waf.bebop.tidy_config_file.format=[string] - -waf.bebop.fancy_xsl_errors.title=Fancy XSL errors -waf.bebop.fancy_xsl_errors.purpose=Always display fancy XSL error pages -waf.bebop.fancy_xsl_errors.example=true|false -waf.bebop.fancy_xsl_errors.format=[boolean] - -waf.bebop.dcp_on_buttons.title=Double Click protection for buttons -waf.bebop.dcp_on_buttons.purpose=Specify whether to apply javascript double click protection to form submits -waf.bebop.dcp_on_buttons.example=true|false -waf.bebop.dcp_on_buttons.format=[boolean] - -waf.bebop.dcp_on_links.title=Double Click protection for links -waf.bebop.dcp_on_links.purpose=Specify whether to apply javascript double click protection to anchor elements -waf.bebop.dcp_on_links.example=true|false -waf.bebop.dcp_on_links.format=[boolean] - -waf.bebop.dhtml_editor.title=DHTML editor implementation -waf.bebop.dhtml_editor.purpose=Select which DHTML text input widget is used for DHTMLEditor Bebop component. -waf.bebop.dhtml_editor.example=FCKeditor -waf.bebop.dhtml_editor.format=Xinha|FCKeditor|HTMLArea - -waf.bebop.dhtml_editor_src.title=Relative path to the source javascript file -waf.bebop.dhtml_editor_src.purpose=Relative path to the source javascript file -waf.bebop.dhtml_editor_src.example=/assets/xinha/XinhaLoader.js -waf.bebop.dhtml_editor_src.format=[String] - -waf.bebop.enable_tree_select_attribute.title=isSelected attribute for bebop.Tree nodes -waf.bebop.enable_tree_select_attribute.purpose=Specify whether to turn on isSelected XML attribute on bebop.Tree nodes (performance impact) -waf.bebop.enable_tree_select_attribute.example=true|false -waf.bebop.enable_tree_select_attribute.format=[boolean]