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, 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.xml.templatemapper; 029 030import org.opencms.ade.containerpage.shared.CmsFormatterConfig; 031import org.opencms.cache.CmsVfsMemoryObjectCache; 032import org.opencms.file.CmsFile; 033import org.opencms.file.CmsObject; 034import org.opencms.file.CmsResourceFilter; 035import org.opencms.gwt.shared.CmsTemplateContextInfo; 036import org.opencms.loader.CmsTemplateContext; 037import org.opencms.loader.CmsTemplateContextManager; 038import org.opencms.loader.I_CmsTemplateContextProvider; 039import org.opencms.main.CmsException; 040import org.opencms.main.CmsLog; 041import org.opencms.main.OpenCms; 042import org.opencms.util.CmsUUID; 043import org.opencms.xml.containerpage.CmsContainerBean; 044import org.opencms.xml.containerpage.CmsContainerElementBean; 045import org.opencms.xml.containerpage.CmsContainerPageBean; 046import org.opencms.xml.containerpage.CmsGroupContainerBean; 047 048import java.io.ByteArrayInputStream; 049import java.util.ArrayList; 050import java.util.HashMap; 051import java.util.HashSet; 052import java.util.List; 053import java.util.Map; 054import java.util.Set; 055 056import javax.servlet.ServletRequest; 057 058import org.apache.commons.collections.Transformer; 059import org.apache.commons.logging.Log; 060 061import org.dom4j.Document; 062import org.dom4j.io.SAXReader; 063 064/** 065 * Responsible for mapping formatters, containers and settings to different formatters, containers and settings according to 066 * the configuration file /system/config/template-mapping.xml.<p> 067 * 068 */ 069public final class CmsTemplateMapper { 070 071 /** The logger instance for this class. */ 072 static final Log LOG = CmsLog.getLog(CmsTemplateMapper.class); 073 074 /** Flag which controls whether this is enabled. */ 075 protected boolean m_enabled; 076 077 /** The path to the mapper configuration. */ 078 protected String m_configPath; 079 080 /** Flag to enable mode for saving. */ 081 private boolean m_forSave; 082 083 /** 084 * Creates a new instance.<p> 085 * 086 * @param configPath the template mapper configuration VFS path 087 */ 088 public CmsTemplateMapper(String configPath) { 089 090 if (configPath != null) { 091 m_enabled = true; 092 m_configPath = configPath; 093 } else { 094 m_enabled = false; 095 } 096 } 097 098 /** 099 * Hidden default constructor, because this is a singleton.<p> 100 */ 101 private CmsTemplateMapper() { 102 103 m_enabled = true; 104 } 105 106 /** 107 * Gets a template mapper. 108 * 109 * @return a template mapper 110 */ 111 public static CmsTemplateMapper get() { 112 113 return new CmsTemplateMapper(); 114 } 115 116 /** 117 * Gets the template mapper for the current request.<p> 118 * 119 * @param request the current request 120 * 121 * @return the template mapper 122 */ 123 public static CmsTemplateMapper get(ServletRequest request) { 124 125 return new CmsTemplateMapper(getTemplateMapperConfig(request)); 126 } 127 128 /** 129 * Checks if the selected template context is "templatemapper". 130 * 131 * @param request the current request 132 * @return true if the selected template context is "templatemapper" 133 */ 134 public static String getTemplateMapperConfig(ServletRequest request) { 135 136 String result = null; 137 CmsTemplateContext templateContext = (CmsTemplateContext)request.getAttribute( 138 CmsTemplateContextManager.ATTR_TEMPLATE_CONTEXT); 139 if (templateContext != null) { 140 I_CmsTemplateContextProvider provider = templateContext.getProvider(); 141 if (provider instanceof I_CmsTemplateMappingContextProvider) { 142 result = ((I_CmsTemplateMappingContextProvider)provider).getMappingConfigurationPath( 143 templateContext.getKey()); 144 } 145 } 146 return result; 147 } 148 149 /** 150 * Sets the for-save mode.<p> 151 * 152 * @param forSave true if for-save mode should be enabled 153 */ 154 public void setForSave(boolean forSave) { 155 156 m_forSave = forSave; 157 } 158 159 /** 160 * Transforms a container page bean.<p> 161 * 162 * @param cms the current CMS context 163 * @param input the bean to be transformed 164 * @param rootPath the root path of the page 165 * 166 * @return the transformed bean 167 */ 168 public CmsContainerPageBean transformContainerpageBean(CmsObject cms, CmsContainerPageBean input, String rootPath) { 169 170 CmsTemplateMapperConfiguration config = getConfiguration(cms); 171 if ((config == null) || !config.isEnabledForPath(rootPath)) { 172 return input; 173 } 174 List<CmsContainerBean> newContainers = new ArrayList<>(); 175 for (CmsContainerBean container : input.getContainers().values()) { 176 List<CmsContainerElementBean> elements = container.getElements(); 177 List<CmsContainerElementBean> newElements = new ArrayList<>(); 178 for (CmsContainerElementBean element : elements) { 179 CmsContainerElementBean newElement = transformContainerElement(cms, config, element); 180 if (newElement != null) { 181 newElements.add(newElement); 182 } 183 } 184 CmsContainerBean newContainer = container.copyWithNewElements(newElements); 185 newContainers.add(newContainer); 186 } 187 CmsContainerPageBean result = new CmsContainerPageBean(newContainers); 188 return result; 189 } 190 191 /** 192 * Transforms a container element bean used for detail elements.<p> 193 * 194 * @param cms the current CMS context 195 * @param input the bean to be transformed 196 * @param rootPath the root path of the page 197 * 198 * @return the transformed bean 199 */ 200 public CmsContainerElementBean transformDetailElement( 201 CmsObject cms, 202 CmsContainerElementBean input, 203 String rootPath) { 204 205 CmsTemplateMapperConfiguration config = getConfiguration(cms); 206 if ((config == null) || !config.isEnabledForPath(rootPath)) { 207 return input; 208 } 209 return transformContainerElement(cms, config, input); 210 211 } 212 213 /** 214 * Transforms a group container bean.<p> 215 * 216 * @param cms the current CMS context 217 * @param input the input bean to be transformed 218 * @param rootPath the root path of the container page 219 * 220 * @return the transformed bean 221 */ 222 public CmsGroupContainerBean transformGroupContainer(CmsObject cms, CmsGroupContainerBean input, String rootPath) { 223 224 CmsTemplateMapperConfiguration config = getConfiguration(cms); 225 if ((config == null) || !config.isEnabledForPath(rootPath)) { 226 return input; 227 } 228 List<CmsContainerElementBean> newElements = new ArrayList<>(); 229 for (CmsContainerElementBean element : input.getElements()) { 230 CmsContainerElementBean newElement = transformContainerElement(cms, config, element); 231 if (newElement != null) { 232 newElements.add(newElement); 233 } 234 } 235 Set<String> transformedTypes = new HashSet<>(); 236 Set<String> oldTypes = input.getTypes(); 237 if (oldTypes == null) { 238 oldTypes = new HashSet<>(); 239 } 240 for (String type : oldTypes) { 241 String newType = config.getMappedElementGroupType(type); 242 if (newType == null) { 243 newType = type; 244 } 245 transformedTypes.add(newType); 246 } 247 248 CmsGroupContainerBean result = new CmsGroupContainerBean( 249 input.getTitle(), 250 input.getDescription(), 251 newElements, 252 transformedTypes); 253 return result; 254 } 255 256 /** 257 * Helper method to transform a single container element.<p> 258 * @param cms the CMS context 259 * @param config the configuration 260 * @param element the container element to be transformed 261 * 262 * @return the transformed bean 263 */ 264 protected CmsContainerElementBean transformContainerElement( 265 CmsObject cms, 266 CmsTemplateMapperConfiguration config, 267 CmsContainerElementBean element) { 268 269 if (m_forSave) { 270 try { 271 element.initResource(cms); 272 } catch (Exception e) { 273 LOG.error(e.getLocalizedMessage(), e); 274 return null; 275 } 276 } 277 Map<String, String> settings = element.getIndividualSettings(); 278 if (settings == null) { 279 settings = new HashMap<>(); 280 } 281 Map<String, String> newSettings = new HashMap<>(); 282 for (Map.Entry<String, String> entry : settings.entrySet()) { 283 String key = entry.getKey(); 284 if (CmsTemplateContextInfo.SETTING.equals(key)) { 285 continue; 286 } 287 String value = entry.getValue(); 288 if (value == null) { 289 continue; 290 } 291 String newValue = value; 292 if (key.startsWith(CmsFormatterConfig.FORMATTER_SETTINGS_KEY)) { 293 if (CmsUUID.isValidUUID(value)) { 294 String newId = config.getMappedFormatterConfiguration(value); 295 if (newId != null) { 296 newValue = newId; 297 } 298 } else if (value.startsWith(CmsFormatterConfig.SCHEMA_FORMATTER_ID)) { 299 String schemaFormatterIdStr = value.substring(CmsFormatterConfig.SCHEMA_FORMATTER_ID.length()); 300 if (CmsUUID.isValidUUID(schemaFormatterIdStr)) { 301 CmsUUID schemaFormatterId = new CmsUUID(schemaFormatterIdStr); 302 CmsUUID mappedFormatterId = config.getMappedFormatterJspId(schemaFormatterId); 303 if (mappedFormatterId != null) { 304 newValue = CmsFormatterConfig.SCHEMA_FORMATTER_ID + mappedFormatterId; 305 } 306 } 307 } 308 } 309 newSettings.put(key, newValue); 310 } 311 CmsContainerElementBean newElement = element.clone(); 312 newElement.updateIndividualSettings(newSettings); 313 CmsUUID formatterId = element.getFormatterId(); 314 if ((formatterId == null) && m_forSave) { 315 try { 316 if (element.isGroupContainer(cms)) { 317 // ID for group-container.jsp 318 formatterId = new CmsUUID("e7029fa2-761e-11e0-bd7f-9ffeadaf4d46"); 319 newElement.setFormatterId(formatterId); 320 } else { 321 if (OpenCms.getResourceManager().matchResourceType("function", element.getResource().getTypeId())) { 322 formatterId = new CmsUUID("087ba7c9-e7fc-4336-acb8-d3416a4eb1fd"); 323 newElement.setFormatterId(formatterId); 324 } 325 } 326 } catch (CmsException e) { 327 LOG.warn(e.getLocalizedMessage(), e); 328 } 329 } 330 CmsUUID mappedFormatterJspId = config.getMappedFormatterJspId(formatterId); 331 if (mappedFormatterJspId != null) { 332 newElement.setFormatterId(mappedFormatterJspId); 333 } 334 return newElement; 335 } 336 337 /** 338 * Loads the configuration file, using CmsVfsMemoryObjectCache for caching. 339 * 340 * @param cms the CMS context 341 * @return the template mapper configuration 342 */ 343 private CmsTemplateMapperConfiguration getConfiguration(final CmsObject cms) { 344 345 if (!m_enabled) { 346 return CmsTemplateMapperConfiguration.EMPTY_CONFIG; 347 } 348 349 if (m_configPath == null) { 350 m_configPath = OpenCms.getSystemInfo().getConfigFilePath(cms, "template-mapping.xml"); 351 } 352 353 return (CmsTemplateMapperConfiguration)(CmsVfsMemoryObjectCache.getVfsMemoryObjectCache().loadVfsObject( 354 cms, 355 m_configPath, 356 new Transformer() { 357 358 @Override 359 public Object transform(Object input) { 360 361 try { 362 CmsFile file = cms.readFile(m_configPath, CmsResourceFilter.IGNORE_EXPIRATION); 363 SAXReader saxBuilder = new SAXReader(); 364 try (ByteArrayInputStream stream = new ByteArrayInputStream(file.getContents())) { 365 Document document = saxBuilder.read(stream); 366 CmsTemplateMapperConfiguration config = new CmsTemplateMapperConfiguration(cms, document); 367 return config; 368 } 369 } catch (Exception e) { 370 LOG.warn(e.getLocalizedMessage(), e); 371 return new CmsTemplateMapperConfiguration(); // empty configuration, does not do anything 372 } 373 374 } 375 })); 376 } 377 378}