001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://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 complex widget. */ 061 private I_CmsComplexWidget m_complexWidget; 062 063 /** The display type. */ 064 private DisplayType m_displayType; 065 066 private String m_placeholder; 067 068 /** A widget instance. */ 069 private I_CmsWidget m_widget; 070 071 /** 072 * Gets the complex widget.<p> 073 * 074 * @return the complex widget 075 */ 076 public I_CmsComplexWidget getComplexWidget() { 077 078 return m_complexWidget; 079 } 080 081 /** 082 * Gets the display type.<p> 083 * 084 * @return the display type 085 */ 086 public DisplayType getDisplayType() { 087 088 return m_displayType; 089 } 090 091 public String getPlaceholder() { 092 093 return m_placeholder; 094 } 095 096 /** 097 * Gets the widget instance.<p> 098 * 099 * @return the widget instance 100 */ 101 public I_CmsWidget getWidget() { 102 103 return m_widget; 104 } 105 106 /** 107 * Sets the complex widget.<p> 108 * 109 * @param complexWidget the complex widget to set 110 */ 111 public void setComplexWidget(I_CmsComplexWidget complexWidget) { 112 113 m_complexWidget = complexWidget; 114 115 } 116 117 /** 118 * Sets the display type.<p> 119 * 120 * @param displayType the display type 121 */ 122 public void setDisplayType(DisplayType displayType) { 123 124 m_displayType = displayType; 125 } 126 127 public void setPlaceholder(String placeholder) { 128 129 m_placeholder = placeholder; 130 } 131 132 /** 133 * Sets the widget.<p> 134 * 135 * @param widget the widget 136 */ 137 public void setWidget(I_CmsWidget widget) { 138 139 m_widget = widget; 140 } 141 } 142 143 /** 144 * Hidden default constructor. 145 */ 146 private CmsWidgetUtil() { 147 148 // hidden default constructor 149 } 150 151 /** 152 * Collects widget information for a given content definition and content value path.<p> 153 * 154 * @param cms the the CMS context to use 155 * @param rootContentDefinition the content definition 156 * @param path the path relative to the given content definition 157 * @param messages the message bundle to use 158 * @param overrideLocale the explicit locale to use for resolving message keys (if null, use the workplace locale of the user) 159 * 160 * @return the widget information for the given path 161 */ 162 public static WidgetInfo collectWidgetInfo( 163 CmsObject cms, 164 CmsXmlContentDefinition rootContentDefinition, 165 String path, 166 CmsMessages messages, 167 Locale overrideLocale) { 168 169 String widgetConfig = null; 170 DisplayType configuredType = DisplayType.none; 171 I_CmsXmlSchemaType schemaType = rootContentDefinition.getSchemaType(path); 172 173 I_CmsWidget widget = null; 174 I_CmsComplexWidget complexWidget = null; 175 I_CmsXmlContentHandler contentHandler = schemaType.getContentDefinition().getContentHandler(); 176 final List<I_CmsWidget> widgets = new ArrayList<>(); 177 final List<String> widgetConfigs = new ArrayList<>(); 178 final List<String> placeholders = new ArrayList<>(); 179 final List<DisplayType> configuredDisplayTypes = new ArrayList<>(); 180 final List<I_CmsComplexWidget> configuredComplexWidgets = new ArrayList<>(); 181 Locale locale = overrideLocale != null ? overrideLocale : OpenCms.getWorkplaceManager().getWorkplaceLocale(cms); 182 if (messages == null) { 183 CmsMultiMessages multi = new CmsMultiMessages(locale); 184 multi.addMessages(OpenCms.getWorkplaceManager().getMessages(locale)); 185 CmsMessages contentHandlerMessages = rootContentDefinition.getContentHandler().getMessages(locale); 186 if (contentHandlerMessages != null) { 187 // Note: the default content handler class will always return a non-null messages object 188 multi.addMessages(contentHandlerMessages); 189 } 190 messages = multi; 191 } 192 193 // Use lists to store found widget configurations, and then use the first elements of each list. 194 // Because we iterate from the top level schema down to the nested schema, configurations in higher level schemas 195 // will have precedence over those in lower level schemas for the same element. 196 197 rootContentDefinition.findSchemaTypesForPath(path, (nestedType, remainingPath) -> { 198 remainingPath = CmsXmlUtils.concatXpath(nestedType.getName(), remainingPath); 199 I_CmsXmlContentHandler handler = nestedType.getContentDefinition().getContentHandler(); 200 I_CmsWidget widgetForPath = handler.getWidget(cms, remainingPath); 201 CollectionUtils.addIgnoreNull(widgets, widgetForPath); 202 CollectionUtils.addIgnoreNull(widgetConfigs, handler.getConfiguration(remainingPath)); 203 CollectionUtils.addIgnoreNull(placeholders, handler.getPlaceholder(remainingPath)); 204 CollectionUtils.addIgnoreNull( 205 configuredDisplayTypes, 206 handler.getConfiguredDisplayType(remainingPath, null)); 207 if (widgetForPath == null) { 208 // If we already have a normal widget, trying to find a complex widget for the same path is unnecessary, 209 // and would also cost performance (because of failing Class.forName calls in getComplexWidget). 210 CollectionUtils.addIgnoreNull(configuredComplexWidgets, handler.getComplexWidget(cms, remainingPath)); 211 } 212 213 }); 214 if (!widgets.isEmpty()) { 215 widget = widgets.get(0).newInstance(); 216 } else { 217 widget = OpenCms.getXmlContentTypeManager().getWidgetDefault(schemaType.getTypeName()); 218 } 219 if (!configuredDisplayTypes.isEmpty()) { 220 configuredType = configuredDisplayTypes.get(0); 221 } 222 if (!widgetConfigs.isEmpty()) { 223 widgetConfig = widgetConfigs.get(0); 224 } else if (widget != null) { 225 widgetConfig = OpenCms.getXmlContentTypeManager().getWidgetDefaultConfiguration(widget); 226 } 227 CmsMacroResolver resolver = new CmsMacroResolver(); 228 resolver.setCmsObject(cms); 229 resolver.setKeepEmptyMacros(false); 230 resolver.setMessages(messages); 231 if (widget != null) { 232 String resolvedConfig = resolveWidgetConfigMacros(resolver, widgetConfig); 233 widget.setConfiguration(resolvedConfig); 234 } 235 String placeholder = ""; 236 if (placeholders.size() > 0) { 237 placeholder = placeholders.get(0); 238 placeholder = resolver.resolveMacros(placeholder); 239 } 240 // default complex widget and default c. widget config have lower priorities than those directly defined, so put them at the end of the list 241 CollectionUtils.addIgnoreNull(configuredComplexWidgets, contentHandler.getDefaultComplexWidget()); 242 List<String> complexWidgetConfigs = new ArrayList<>(widgetConfigs); 243 CollectionUtils.addIgnoreNull(complexWidgetConfigs, contentHandler.getDefaultComplexWidgetConfiguration()); 244 if (!configuredComplexWidgets.isEmpty()) { 245 String config = ""; 246 if (!complexWidgetConfigs.isEmpty()) { 247 config = complexWidgetConfigs.get(0); 248 config = resolveWidgetConfigMacros(resolver, config); 249 } 250 complexWidget = configuredComplexWidgets.get(0).configure(config); 251 } 252 WidgetInfo result = new WidgetInfo(); 253 result.setComplexWidget(complexWidget); 254 result.setDisplayType(configuredType); 255 result.setWidget(widget); 256 result.setPlaceholder(placeholder); 257 return result; 258 } 259 260 /** 261 * Collects widget information for a given content value.<p> 262 * 263 * @param cms the current CMS context 264 * @param value a content value 265 * 266 * @return the widget information for the given value 267 */ 268 269 public static WidgetInfo collectWidgetInfo(CmsObject cms, I_CmsXmlContentValue value) { 270 271 CmsXmlContentDefinition contentDef = value.getDocument().getContentDefinition(); 272 String path = value.getPath(); 273 return collectWidgetInfo(cms, contentDef, path, null, null); 274 } 275 276 /** 277 * 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. 278 * 279 * @param resolver the macro resolver 280 * @param widgetConfig the widget configuration 281 * @return the macro resolution result 282 */ 283 private static String resolveWidgetConfigMacros(CmsMacroResolver resolver, String widgetConfig) { 284 285 if (Boolean.parseBoolean((String)OpenCms.getRuntimeProperty("widgets.config.resolveMacros.disabled"))) { 286 return widgetConfig; 287 } else { 288 return resolver.resolveMacros(widgetConfig); 289 } 290 } 291 292}