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.workplace.editors; 029 030import org.opencms.main.CmsLog; 031import org.opencms.util.CmsStringUtil; 032import org.opencms.xml.CmsXmlException; 033import org.opencms.xml.CmsXmlUtils; 034 035import java.util.ArrayList; 036import java.util.HashMap; 037import java.util.Iterator; 038import java.util.List; 039import java.util.Map; 040import java.util.regex.Pattern; 041import java.util.regex.PatternSyntaxException; 042 043import org.apache.commons.logging.Log; 044 045import org.dom4j.Document; 046import org.dom4j.Element; 047 048/** 049 * Single editor configuration object.<p> 050 * 051 * Holds all necessary information about an OpenCms editor which is stored in the 052 * "editor_configuration.xml" file in each editor folder.<p> 053 * 054 * Provides methods to get the editor information for the editor manager.<p> 055 * 056 * @since 6.0.0 057 */ 058public class CmsWorkplaceEditorConfiguration { 059 060 /** Name of the root document node. */ 061 public static final String DOCUMENT_NODE = "editor"; 062 063 /** Name of the name attribute. */ 064 protected static final String A_NAME = "name"; 065 066 /** Name of the single user agent node. */ 067 protected static final String N_AGENT = "agent"; 068 069 /** Name of the resource type class node. */ 070 protected static final String N_CLASS = "class"; 071 072 /** Name of the editor label node. */ 073 protected static final String N_LABEL = "label"; 074 075 /** Name of the resource type subnode mapto. */ 076 protected static final String N_MAPTO = "mapto"; 077 078 /** Name of the resource type subnode name. */ 079 protected static final String N_NAME = "name"; 080 081 /** Name of the param node. */ 082 protected static final String N_PARAM = "param"; 083 084 /** Name of the params node. */ 085 protected static final String N_PARAMS = "params"; 086 087 /** Name of the resource type subnode ranking. */ 088 protected static final String N_RANKING = "ranking"; 089 090 /** Name of the resourcetypes node. */ 091 protected static final String N_RESOURCETYPES = "resourcetypes"; 092 093 /** Name of the resource type node. */ 094 protected static final String N_TYPE = "type"; 095 096 /** Name of the useragents node. */ 097 protected static final String N_USERAGENTS = "useragents"; 098 099 /** Name of the widgeteditor node. */ 100 protected static final String N_WIDGETEDITOR = "widgeteditor"; 101 102 /** The log object for this class. */ 103 private static final Log LOG = CmsLog.getLog(CmsWorkplaceEditorConfiguration.class); 104 105 /** The browser patterns. */ 106 private List<Pattern> m_browserPattern; 107 108 /** The editor label. */ 109 private String m_editorLabel; 110 111 /** The editor URI. */ 112 private String m_editorUri; 113 114 /** The name of the configuration (usually the name of the folder under /system/workplace/editors). */ 115 private String m_name; 116 117 /** Additional parameters for the editor. */ 118 private Map<String, String> m_parameters = new HashMap<String, String>(); 119 120 /** The resource types. */ 121 private Map<String, String[]> m_resTypes; 122 123 /** The user agents. */ 124 private List<String> m_userAgentsRegEx; 125 126 /** The valid configuration flag. */ 127 private boolean m_validConfiguration; 128 129 /** The widget editor. */ 130 private String m_widgetEditor; 131 132 /** 133 * Constructor with xml data String.<p> 134 * 135 * @param xmlData the XML data String containing the information about the editor 136 * @param editorUri the editor workplace URI 137 * @param name the editor configuration name 138 */ 139 public CmsWorkplaceEditorConfiguration(byte[] xmlData, String editorUri, String name) { 140 141 setValidConfiguration(true); 142 try { 143 m_name = name; 144 initialize(CmsXmlUtils.unmarshalHelper(xmlData, null), editorUri); 145 146 } catch (CmsXmlException e) { 147 // xml String could not be parsed 148 logConfigurationError(Messages.get().getBundle().key(Messages.ERR_XML_PARSE_0), e); 149 } 150 } 151 152 /** 153 * Returns the list of compiled browser patterns.<p> 154 * 155 * @return the list of compiled browser patterns 156 */ 157 public List<Pattern> getBrowserPattern() { 158 159 return m_browserPattern; 160 } 161 162 /** 163 * Returns the editor label key used for the localized nice name.<p> 164 * 165 * @return the editor label key used for the localized nice name 166 */ 167 public String getEditorLabel() { 168 169 return m_editorLabel; 170 } 171 172 /** 173 * Returns the editor workplace URI.<p> 174 * 175 * @return the editor workplace URI 176 */ 177 public String getEditorUri() { 178 179 return m_editorUri; 180 } 181 182 /** 183 * Returns the mapping for the given resource type.<p> 184 * 185 * @param resourceType the resource type name to check 186 * @return the mapping or null, if no mapping is specified 187 */ 188 public String getMappingForResourceType(String resourceType) { 189 190 String[] resourceTypeParams = getResourceTypes().get(resourceType); 191 if (resourceTypeParams == null) { 192 return null; 193 } else { 194 return resourceTypeParams[1]; 195 } 196 } 197 198 /** 199 * Gets the name of the editor configuration (usually the folder name under /system/workplace/editors).<p> 200 * 201 * @return the name of the editor configuration 202 */ 203 public String getName() { 204 205 return m_name; 206 } 207 208 /** 209 * Gets the map of additional editor parameters.<p> 210 * 211 * @return the editor parameter map 212 */ 213 public Map<String, String> getParameters() { 214 215 return m_parameters; 216 } 217 218 /** 219 * Returns the ranking value for the given resource type.<p> 220 * 221 * @param resourceType the current resource type 222 * @return the ranking (the higher the better) 223 */ 224 public float getRankingForResourceType(String resourceType) { 225 226 String[] resourceTypeParams = getResourceTypes().get(resourceType); 227 if (resourceTypeParams == null) { 228 return -1.0f; 229 } else { 230 return Float.parseFloat(resourceTypeParams[0]); 231 } 232 } 233 234 /** 235 * Returns the valid resource types of the editor.<p> 236 * 237 * A single map item has the resource type name as key, 238 * the value is a String array with two entries: 239 * <ul> 240 * <li>Entry 0: the ranking for the resource type</li> 241 * <li>Entry 1: the mapping to another resource type or null</li> 242 * </ul><p> 243 * 244 * @return the valid resource types of the editor 245 */ 246 public Map<String, String[]> getResourceTypes() { 247 248 return m_resTypes; 249 } 250 251 /** 252 * Returns the valid user agents regular expressions of the editor.<p> 253 * 254 * @return the valid user agents regular expressions of the editor 255 */ 256 public List<String> getUserAgentsRegEx() { 257 258 return m_userAgentsRegEx; 259 } 260 261 /** 262 * Returns the widget editor class for rich text editing.<p> 263 * 264 * @return the widget editor class for rich text editing 265 */ 266 public String getWidgetEditor() { 267 268 return m_widgetEditor; 269 } 270 271 /** 272 * Returns if the current configuration is valid.<p> 273 * 274 * @return true if no configuration errors were found, otherwise false 275 */ 276 public boolean isValidConfiguration() { 277 278 return m_validConfiguration; 279 } 280 281 /** 282 * Returns if the editor is usable as a widget editor for rich text editing.<p> 283 * 284 * @return true if the editor is usable as a widget editor for rich text editing, otherwise false 285 */ 286 public boolean isWidgetEditor() { 287 288 return CmsStringUtil.isNotEmpty(m_widgetEditor); 289 } 290 291 /** 292 * Tests if the current browser is matching the configuration.<p> 293 * 294 * @param currentBrowser the users browser String to test 295 * @return true if the browser matches the configuration, otherwise false 296 */ 297 public boolean matchesBrowser(String currentBrowser) { 298 299 if (currentBrowser == null) { 300 return false; 301 } 302 for (int i = 0; i < getBrowserPattern().size(); i++) { 303 boolean matches = getBrowserPattern().get(i).matcher(currentBrowser.trim()).matches(); 304 if (matches) { 305 if (LOG.isDebugEnabled()) { 306 LOG.debug(Messages.get().getBundle().key(Messages.LOG_BROWSER_MATCHES_CONFIG_1, currentBrowser)); 307 } 308 return true; 309 } 310 } 311 return false; 312 } 313 314 /** 315 * Returns if the configuration is suitable for the given resource type.<p> 316 * 317 * @param resourceType the resource type to check 318 * @return true if the configuration matches the resource type 319 */ 320 public boolean matchesResourceType(String resourceType) { 321 322 return m_resTypes.containsKey(resourceType); 323 } 324 325 /** 326 * Initializes all member variables.<p> 327 * 328 * @param document the XML configuration document 329 * @param editorUri the editor workplace URI 330 */ 331 @SuppressWarnings("unchecked") 332 private void initialize(Document document, String editorUri) { 333 334 m_parameters.clear(); 335 336 // get the root element of the configuration 337 Element rootElement = document.getRootElement(); 338 339 // set the label of the editor 340 setEditorLabel(rootElement.elementText(N_LABEL)); 341 342 // set the widget editor class if available 343 String widgetClass = rootElement.elementText(N_WIDGETEDITOR); 344 if (CmsStringUtil.isNotEmpty(widgetClass)) { 345 setWidgetEditor(widgetClass); 346 } 347 348 // set the URI of the editor 349 setEditorUri(editorUri); 350 351 // create the map of valid resource types 352 Iterator<Element> i = rootElement.element(N_RESOURCETYPES).elementIterator(N_TYPE); 353 Map<String, String[]> resTypes = new HashMap<String, String[]>(); 354 while (i.hasNext()) { 355 Element currentType = i.next(); 356 float ranking; 357 String name = currentType.elementText(N_NAME); 358 if (CmsStringUtil.isEmpty(name)) { 359 logConfigurationError(Messages.get().getBundle().key(Messages.ERR_INVALID_RESTYPE_NAME_0), null); 360 continue; 361 } 362 try { 363 ranking = Float.parseFloat(currentType.elementText(N_RANKING)); 364 } catch (Throwable t) { 365 logConfigurationError(Messages.get().getBundle().key(Messages.ERR_INVALID_RESTYPE_RANKING_1, name), t); 366 continue; 367 } 368 String mapTo = currentType.elementText(N_MAPTO); 369 if (CmsStringUtil.isEmpty(mapTo)) { 370 mapTo = null; 371 } 372 resTypes.put(name, new String[] {"" + ranking, mapTo}); 373 } 374 // add the additional resource types 375 i = rootElement.element(N_RESOURCETYPES).elementIterator(N_CLASS); 376 while (i.hasNext()) { 377 Element currentClass = i.next(); 378 String name = currentClass.elementText(N_NAME); 379 List<String> assignedTypes = new ArrayList<String>(); 380 try { 381 // get the editor type matcher class 382 I_CmsEditorTypeMatcher matcher = (I_CmsEditorTypeMatcher)Class.forName(name).newInstance(); 383 assignedTypes = matcher.getAdditionalResourceTypes(); 384 } catch (Throwable t) { 385 logConfigurationError(Messages.get().getBundle().key(Messages.ERR_INVALID_RESTYPE_CLASS_1, name), t); 386 continue; 387 } 388 float ranking; 389 try { 390 ranking = Float.parseFloat(currentClass.elementText(N_RANKING)); 391 } catch (Throwable t) { 392 logConfigurationError(Messages.get().getBundle().key(Messages.ERR_INVALID_RESTYPE_RANKING_1, name), t); 393 continue; 394 } 395 String mapTo = currentClass.elementText(N_MAPTO); 396 if ("".equals(mapTo)) { 397 mapTo = null; 398 } 399 // now loop through all types found and add them 400 Iterator<String> j = assignedTypes.iterator(); 401 while (j.hasNext()) { 402 String typeName = j.next(); 403 resTypes.put(typeName, new String[] {"" + ranking, mapTo}); 404 } 405 } 406 407 setResourceTypes(resTypes); 408 409 // create the list of user agents & compiled patterns for editor 410 i = document.getRootElement().element(N_USERAGENTS).elementIterator(N_AGENT); 411 List<Pattern> pattern = new ArrayList<Pattern>(); 412 List<String> userAgents = new ArrayList<String>(); 413 while (i.hasNext()) { 414 Element currentAgent = i.next(); 415 String agentName = currentAgent.getText(); 416 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(agentName)) { 417 userAgents.add(agentName); 418 try { 419 pattern.add(Pattern.compile(agentName)); 420 } catch (PatternSyntaxException e) { 421 logConfigurationError( 422 Messages.get().getBundle().key(Messages.ERR_COMPILE_EDITOR_REGEX_1, agentName), 423 e); 424 } 425 } else { 426 logConfigurationError(Messages.get().getBundle().key(Messages.ERR_INVALID_USERAGENT_DEF_0), null); 427 } 428 } 429 setBrowserPattern(pattern); 430 setUserAgentsRegEx(userAgents); 431 432 Element paramsElement = (Element)(document.getRootElement().selectSingleNode(N_PARAMS)); 433 if (paramsElement != null) { 434 List<?> params = paramsElement.selectNodes(N_PARAM); 435 for (Object paramObj : params) { 436 Element paramElement = (Element)paramObj; 437 String name = paramElement.attributeValue(A_NAME); 438 String value = paramElement.getText(); 439 m_parameters.put(name, value); 440 } 441 } 442 } 443 444 /** 445 * Logs configuration errors and invalidates the current configuration.<p> 446 * 447 * @param message the message specifying the configuration error 448 * @param t the Throwable object or null 449 */ 450 private void logConfigurationError(String message, Throwable t) { 451 452 setValidConfiguration(false); 453 if (LOG.isErrorEnabled()) { 454 if (t == null) { 455 LOG.error(Messages.get().getBundle().key(Messages.LOG_EDITOR_CONFIG_ERROR_1, message)); 456 } else { 457 LOG.error(Messages.get().getBundle().key(Messages.LOG_EDITOR_CONFIG_ERROR_1, message), t); 458 } 459 } 460 } 461 462 /** 463 * Sets the list of compiled browser patterns.<p> 464 * 465 * @param pattern the list of compiled browser patterns 466 */ 467 private void setBrowserPattern(List<Pattern> pattern) { 468 469 if ((pattern == null) || (pattern.size() == 0)) { 470 setValidConfiguration(false); 471 LOG.error(Messages.get().getBundle().key(Messages.LOG_EDITOR_CONFIG_NO_PATTERN_0)); 472 } 473 m_browserPattern = pattern; 474 } 475 476 /** 477 * Sets the editor label key used for the localized nice name.<p> 478 * 479 * @param label the editor label key used for the localized nice name 480 */ 481 private void setEditorLabel(String label) { 482 483 if (CmsStringUtil.isEmptyOrWhitespaceOnly(label)) { 484 setValidConfiguration(false); 485 LOG.error(Messages.get().getBundle().key(Messages.LOG_EDITOR_CONFIG_NO_LABEL_0)); 486 } 487 m_editorLabel = label; 488 } 489 490 /** 491 * Sets the editor workplace URI.<p> 492 * @param uri the editor workplace URI 493 */ 494 private void setEditorUri(String uri) { 495 496 if (CmsStringUtil.isEmptyOrWhitespaceOnly(uri)) { 497 setValidConfiguration(false); 498 LOG.error(Messages.get().getBundle().key(Messages.LOG_EDITOR_CONFIG_NO_URI_0)); 499 } 500 m_editorUri = uri; 501 } 502 503 /** 504 * Sets the valid resource types of the editor.<p> 505 * 506 * @param types the valid resource types of the editor 507 */ 508 private void setResourceTypes(Map<String, String[]> types) { 509 510 if ((types == null) || (types.size() == 0)) { 511 setValidConfiguration(false); 512 LOG.error(Messages.get().getBundle().key(Messages.LOG_NO_RESOURCE_TYPES_0)); 513 } 514 m_resTypes = types; 515 } 516 517 /** 518 * Sets the valid user agents regular expressions of the editor.<p> 519 * 520 * @param agents the valid user agents regular expressions of the editor 521 */ 522 private void setUserAgentsRegEx(List<String> agents) { 523 524 if ((agents == null) || (agents.size() == 0)) { 525 setValidConfiguration(false); 526 LOG.error(Messages.get().getBundle().key(Messages.LOG_NO_USER_AGENTS_0)); 527 } 528 m_userAgentsRegEx = agents; 529 } 530 531 /** 532 * Sets if the current configuration is valid.<p> 533 * 534 * @param isValid true if no configuration errors were found, otherwise false 535 */ 536 private void setValidConfiguration(boolean isValid) { 537 538 m_validConfiguration = isValid; 539 } 540 541 /** 542 * Sets the widget editor class for rich text editing.<p> 543 * 544 * @param widgetEditor the widget editor class for rich text editing 545 */ 546 private void setWidgetEditor(String widgetEditor) { 547 548 m_widgetEditor = widgetEditor; 549 } 550 551}