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.ade.contenteditor; 029 030import org.opencms.file.CmsObject; 031import org.opencms.i18n.CmsMessages; 032import org.opencms.i18n.CmsMultiMessages; 033import org.opencms.main.OpenCms; 034import org.opencms.util.CmsMacroResolver; 035import org.opencms.widgets.I_CmsComplexWidget; 036import org.opencms.widgets.I_CmsWidget; 037import org.opencms.xml.CmsXmlContentDefinition; 038import org.opencms.xml.CmsXmlUtils; 039import org.opencms.xml.content.I_CmsXmlContentHandler; 040import org.opencms.xml.content.I_CmsXmlContentHandler.DisplayType; 041import org.opencms.xml.types.I_CmsXmlContentValue; 042import org.opencms.xml.types.I_CmsXmlSchemaType; 043 044import java.util.ArrayList; 045import java.util.List; 046import java.util.Locale; 047 048import org.apache.commons.collections4.CollectionUtils; 049 050/** 051 * Utility methods for getting widget informations out of content definitions.<p> 052 */ 053public final class CmsWidgetUtil { 054 055 /** 056 * Bean representing widget information.<p> 057 */ 058 public static class WidgetInfo { 059 060 /** The display type. */ 061 private DisplayType m_displayType; 062 063 /** A widget instance. */ 064 private I_CmsWidget m_widget; 065 066 /** The complex widget. */ 067 private I_CmsComplexWidget m_complexWidget; 068 069 /** 070 * Gets the complex widget.<p> 071 * 072 * @return the complex widget 073 */ 074 public I_CmsComplexWidget getComplexWidget() { 075 076 return m_complexWidget; 077 } 078 079 /** 080 * Gets the display type.<p> 081 * 082 * @return the display type 083 */ 084 public DisplayType getDisplayType() { 085 086 return m_displayType; 087 } 088 089 /** 090 * Gets the widget instance.<p> 091 * 092 * @return the widget instance 093 */ 094 public I_CmsWidget getWidget() { 095 096 return m_widget; 097 } 098 099 /** 100 * Sets the complex widget.<p> 101 * 102 * @param complexWidget the complex widget to set 103 */ 104 public void setComplexWidget(I_CmsComplexWidget complexWidget) { 105 106 m_complexWidget = complexWidget; 107 108 } 109 110 /** 111 * Sets the display type.<p> 112 * 113 * @param displayType the display type 114 */ 115 public void setDisplayType(DisplayType displayType) { 116 117 m_displayType = displayType; 118 } 119 120 /** 121 * Sets the widget.<p> 122 * 123 * @param widget the widget 124 */ 125 public void setWidget(I_CmsWidget widget) { 126 127 m_widget = widget; 128 } 129 } 130 131 /** 132 * Hidden default constructor. 133 */ 134 private CmsWidgetUtil() { 135 136 // hidden default constructor 137 } 138 139 /** 140 * Collects widget information for a given content definition and content value path.<p> 141 * 142 * @param cms the the CMS context to use 143 * @param rootContentDefinition the content definition 144 * @param path the path relative to the given content definition 145 * @param messages the message bundle to use 146 * @param overrideLocale the explicit locale to use for resolving message keys (if null, use the workplace locale of the user) 147 * 148 * @return the widget information for the given path 149 */ 150 public static WidgetInfo collectWidgetInfo( 151 CmsObject cms, 152 CmsXmlContentDefinition rootContentDefinition, 153 String path, 154 CmsMessages messages, 155 Locale overrideLocale) { 156 157 String widgetConfig = null; 158 DisplayType configuredType = DisplayType.none; 159 I_CmsXmlSchemaType schemaType = rootContentDefinition.getSchemaType(path); 160 161 I_CmsWidget widget = null; 162 I_CmsComplexWidget complexWidget = null; 163 I_CmsXmlContentHandler contentHandler = schemaType.getContentDefinition().getContentHandler(); 164 final List<I_CmsWidget> widgets = new ArrayList<>(); 165 final List<String> widgetConfigs = new ArrayList<>(); 166 final List<DisplayType> configuredDisplayTypes = new ArrayList<>(); 167 final List<I_CmsComplexWidget> configuredComplexWidgets = new ArrayList<>(); 168 Locale locale = overrideLocale != null ? overrideLocale : OpenCms.getWorkplaceManager().getWorkplaceLocale(cms); 169 if (messages == null) { 170 CmsMultiMessages multi = new CmsMultiMessages(locale); 171 multi.addMessages(OpenCms.getWorkplaceManager().getMessages(locale)); 172 CmsMessages contentHandlerMessages = rootContentDefinition.getContentHandler().getMessages(locale); 173 if (contentHandlerMessages != null) { 174 // Note: the default content handler class will always return a non-null messages object 175 multi.addMessages(contentHandlerMessages); 176 } 177 messages = multi; 178 } 179 180 // Use lists to store found widget configurations, and then use the first elements of each list. 181 // Because we iterate from the top level schema down to the nested schema, configurations in higher level schemas 182 // will have precedence over those in lower level schemas for the same element. 183 184 rootContentDefinition.findSchemaTypesForPath(path, (nestedType, remainingPath) -> { 185 remainingPath = CmsXmlUtils.concatXpath(nestedType.getName(), remainingPath); 186 I_CmsXmlContentHandler handler = nestedType.getContentDefinition().getContentHandler(); 187 I_CmsWidget widgetForPath = handler.getWidget(cms, remainingPath); 188 CollectionUtils.addIgnoreNull(widgets, widgetForPath); 189 CollectionUtils.addIgnoreNull(widgetConfigs, handler.getConfiguration(remainingPath)); 190 CollectionUtils.addIgnoreNull( 191 configuredDisplayTypes, 192 handler.getConfiguredDisplayType(remainingPath, null)); 193 if (widgetForPath == null) { 194 // If we already have a normal widget, trying to find a complex widget for the same path is unnecessary, 195 // and would also cost performance (because of failing Class.forName calls in getComplexWidget). 196 CollectionUtils.addIgnoreNull(configuredComplexWidgets, handler.getComplexWidget(cms, remainingPath)); 197 } 198 199 }); 200 if (!widgets.isEmpty()) { 201 widget = widgets.get(0).newInstance(); 202 } else { 203 widget = OpenCms.getXmlContentTypeManager().getWidgetDefault(schemaType.getTypeName()); 204 } 205 if (!configuredDisplayTypes.isEmpty()) { 206 configuredType = configuredDisplayTypes.get(0); 207 } 208 if (!widgetConfigs.isEmpty()) { 209 widgetConfig = widgetConfigs.get(0); 210 } else if (widget != null) { 211 widgetConfig = OpenCms.getXmlContentTypeManager().getWidgetDefaultConfiguration(widget); 212 } 213 CmsMacroResolver resolver = new CmsMacroResolver(); 214 resolver.setCmsObject(cms); 215 resolver.setKeepEmptyMacros(false); 216 resolver.setMessages(messages); 217 if (widget != null) { 218 String resolvedConfig = resolveWidgetConfigMacros(resolver, widgetConfig); 219 widget.setConfiguration(resolvedConfig); 220 } 221 // default complex widget and default c. widget config have lower priorities than those directly defined, so put them at the end of the list 222 CollectionUtils.addIgnoreNull(configuredComplexWidgets, contentHandler.getDefaultComplexWidget()); 223 List<String> complexWidgetConfigs = new ArrayList<>(widgetConfigs); 224 CollectionUtils.addIgnoreNull(complexWidgetConfigs, contentHandler.getDefaultComplexWidgetConfiguration()); 225 if (!configuredComplexWidgets.isEmpty()) { 226 String config = ""; 227 if (!complexWidgetConfigs.isEmpty()) { 228 config = complexWidgetConfigs.get(0); 229 config = resolveWidgetConfigMacros(resolver, config); 230 } 231 complexWidget = configuredComplexWidgets.get(0).configure(config); 232 } 233 WidgetInfo result = new WidgetInfo(); 234 result.setComplexWidget(complexWidget); 235 result.setDisplayType(configuredType); 236 result.setWidget(widget); 237 return result; 238 } 239 240 /** 241 * Collects widget information for a given content value.<p> 242 * 243 * @param cms the current CMS context 244 * @param value a content value 245 * 246 * @return the widget information for the given value 247 */ 248 249 public static WidgetInfo collectWidgetInfo(CmsObject cms, I_CmsXmlContentValue value) { 250 251 CmsXmlContentDefinition contentDef = value.getDocument().getContentDefinition(); 252 String path = value.getPath(); 253 return collectWidgetInfo(cms, contentDef, path, null, null); 254 } 255 256 /** 257 * Resolves macros in a string using the given macro resolver, unless universal macro resolution for widget configurations is turned off by setting the widgets.config.resolveMacros.disabled runtime property to true in opencms-system.xml. 258 * 259 * @param resolver the macro resolver 260 * @param widgetConfig the widget configuration 261 * @return the macro resolution result 262 */ 263 private static String resolveWidgetConfigMacros(CmsMacroResolver resolver, String widgetConfig) { 264 265 if (Boolean.parseBoolean((String)OpenCms.getRuntimeProperty("widgets.config.resolveMacros.disabled"))) { 266 return widgetConfig; 267 } else { 268 return resolver.resolveMacros(widgetConfig); 269 } 270 } 271 272}