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 * 147 * @return the widget information for the given path 148 */ 149 public static WidgetInfo collectWidgetInfo( 150 CmsObject cms, 151 CmsXmlContentDefinition rootContentDefinition, 152 String path, 153 CmsMessages messages) { 154 155 String widgetConfig = null; 156 DisplayType configuredType = DisplayType.none; 157 I_CmsXmlSchemaType schemaType = rootContentDefinition.getSchemaType(path); 158 159 I_CmsWidget widget = null; 160 I_CmsComplexWidget complexWidget = null; 161 I_CmsXmlContentHandler contentHandler = schemaType.getContentDefinition().getContentHandler(); 162 final List<I_CmsWidget> widgets = new ArrayList<>(); 163 final List<String> widgetConfigs = new ArrayList<>(); 164 final List<DisplayType> configuredDisplayTypes = new ArrayList<>(); 165 final List<I_CmsComplexWidget> configuredComplexWidgets = new ArrayList<>(); 166 if (messages == null) { 167 Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms); 168 CmsMultiMessages multi = new CmsMultiMessages(wpLocale); 169 multi.addMessages(OpenCms.getWorkplaceManager().getMessages(wpLocale)); 170 CmsMessages contentHandlerMessages = rootContentDefinition.getContentHandler().getMessages(wpLocale); 171 if (contentHandlerMessages != null) { 172 // Note: the default content handler class will always return a non-null messages object 173 multi.addMessages(contentHandlerMessages); 174 } 175 messages = multi; 176 } 177 178 // Use lists to store found widget configurations, and then use the first elements of each list. 179 // Because we iterate from the top level schema down to the nested schema, configurations in higher level schemas 180 // will have precedence over those in lower level schemas for the same element. 181 182 rootContentDefinition.findSchemaTypesForPath(path, (nestedType, remainingPath) -> { 183 remainingPath = CmsXmlUtils.concatXpath(nestedType.getName(), remainingPath); 184 I_CmsXmlContentHandler handler = nestedType.getContentDefinition().getContentHandler(); 185 I_CmsWidget widgetForPath = handler.getWidget(cms, remainingPath); 186 CollectionUtils.addIgnoreNull(widgets, widgetForPath); 187 CollectionUtils.addIgnoreNull(widgetConfigs, handler.getConfiguration(remainingPath)); 188 CollectionUtils.addIgnoreNull( 189 configuredDisplayTypes, 190 handler.getConfiguredDisplayType(remainingPath, null)); 191 if (widgetForPath == null) { 192 // If we already have a normal widget, trying to find a complex widget for the same path is unnecessary, 193 // and would also cost performance (because of failing Class.forName calls in getComplexWidget). 194 CollectionUtils.addIgnoreNull(configuredComplexWidgets, handler.getComplexWidget(cms, remainingPath)); 195 } 196 197 }); 198 if (!widgets.isEmpty()) { 199 widget = widgets.get(0).newInstance(); 200 } else { 201 widget = OpenCms.getXmlContentTypeManager().getWidgetDefault(schemaType.getTypeName()); 202 } 203 if (!configuredDisplayTypes.isEmpty()) { 204 configuredType = configuredDisplayTypes.get(0); 205 } 206 if (!widgetConfigs.isEmpty()) { 207 widgetConfig = widgetConfigs.get(0); 208 } else if (widget != null) { 209 widgetConfig = OpenCms.getXmlContentTypeManager().getWidgetDefaultConfiguration(widget); 210 } 211 CmsMacroResolver resolver = new CmsMacroResolver(); 212 resolver.setCmsObject(cms); 213 resolver.setKeepEmptyMacros(false); 214 resolver.setMessages(messages); 215 if (widget != null) { 216 String resolvedConfig = resolveWidgetConfigMacros(resolver, widgetConfig); 217 widget.setConfiguration(resolvedConfig); 218 } 219 // default complex widget and default c. widget config have lower priorities than those directly defined, so put them at the end of the list 220 CollectionUtils.addIgnoreNull(configuredComplexWidgets, contentHandler.getDefaultComplexWidget()); 221 List<String> complexWidgetConfigs = new ArrayList<>(widgetConfigs); 222 CollectionUtils.addIgnoreNull(complexWidgetConfigs, contentHandler.getDefaultComplexWidgetConfiguration()); 223 if (!configuredComplexWidgets.isEmpty()) { 224 String config = ""; 225 if (!complexWidgetConfigs.isEmpty()) { 226 config = complexWidgetConfigs.get(0); 227 config = resolveWidgetConfigMacros(resolver, config); 228 } 229 complexWidget = configuredComplexWidgets.get(0).configure(config); 230 } 231 WidgetInfo result = new WidgetInfo(); 232 result.setComplexWidget(complexWidget); 233 result.setDisplayType(configuredType); 234 result.setWidget(widget); 235 return result; 236 } 237 238 /** 239 * Collects widget information for a given content value.<p> 240 * 241 * @param cms the current CMS context 242 * @param value a content value 243 * 244 * @return the widget information for the given value 245 */ 246 247 public static WidgetInfo collectWidgetInfo(CmsObject cms, I_CmsXmlContentValue value) { 248 249 CmsXmlContentDefinition contentDef = value.getDocument().getContentDefinition(); 250 String path = value.getPath(); 251 return collectWidgetInfo(cms, contentDef, path, null); 252 } 253 254 /** 255 * 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. 256 * 257 * @param resolver the macro resolver 258 * @param widgetConfig the widget configuration 259 * @return the macro resolution result 260 */ 261 private static String resolveWidgetConfigMacros(CmsMacroResolver resolver, String widgetConfig) { 262 263 if (Boolean.parseBoolean((String)OpenCms.getRuntimeProperty("widgets.config.resolveMacros.disabled"))) { 264 return widgetConfig; 265 } else { 266 return resolver.resolveMacros(widgetConfig); 267 } 268 } 269 270}