001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH (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, 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.widgets; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsResource; 032import org.opencms.i18n.CmsEncoder; 033import org.opencms.i18n.CmsLocaleManager; 034import org.opencms.i18n.CmsMessages; 035import org.opencms.util.CmsMacroResolver; 036import org.opencms.util.CmsStringUtil; 037import org.opencms.util.I_CmsMacroResolver; 038import org.opencms.xml.content.I_CmsXmlContentHandler.DisplayType; 039import org.opencms.xml.types.A_CmsXmlContentValue; 040import org.opencms.xml.types.I_CmsXmlContentValue; 041 042import java.util.Iterator; 043import java.util.List; 044import java.util.Locale; 045import java.util.Map; 046import java.util.regex.Matcher; 047import java.util.regex.Pattern; 048 049/** 050 * Provides a standard HTML form input widget for overwriting localized values of a resource bundle, for use on a widget dialog.<p> 051 * 052 * The resource bundle is configured with the widget configuration attribute. An optional key name to look up in the bundle 053 * can be given, too, in case it is different from the element name: <code>key=mykey</code>.<p> 054 * 055 * The locale to get the value for can be configured, too, by adding a configuration directive: <code>locale=en</code>.<p> 056 * 057 * Example: <code><layout element="elemname" widget="LocalizationWidget" configuration="org.opencms.workplace.messages|key=mykey|locale=en" /></code>.<p> 058 * 059 * To use the stored localization values and have the values of the resource bundles as fallback, 060 * use the {@link org.opencms.xml.CmsXmlMessages} object.<p> 061 * 062 * @since 6.5.4 063 */ 064public class CmsLocalizationWidget extends A_CmsWidget implements I_CmsADEWidget { 065 066 /** The option for the localized key name. */ 067 public static final String OPTION_KEY = "key="; 068 069 /** The option for the locale to use. */ 070 public static final String OPTION_LOCALE = "locale="; 071 072 /** Pattern to get OpenCms like macros, e.g. "%(0)". */ 073 private static Pattern PATTERN_MACRO = Pattern.compile(".*(" 074 + I_CmsMacroResolver.MACRO_DELIMITER 075 + "\\" 076 + I_CmsMacroResolver.MACRO_START 077 + ")(\\d*)(\\" 078 + I_CmsMacroResolver.MACRO_END 079 + ").*"); 080 081 /** Pattern to get message bundle arguments, e.g. "{0}". */ 082 private static Pattern PATTERN_MESSAGEARGUMENT = Pattern.compile(".*(\\{)(\\d*)(\\}).*"); 083 084 /** The bundle key (optional, if not equal to the element name). */ 085 private String m_bundleKey; 086 087 /** The locale to get the value for. */ 088 private Locale m_locale; 089 090 /** The localized bundle to get the value from. */ 091 private CmsMessages m_messages; 092 093 /** 094 * Creates a new input localization widget.<p> 095 */ 096 public CmsLocalizationWidget() { 097 098 // empty constructor is required for class registration 099 this(""); 100 } 101 102 /** 103 * Creates a new input localization widget with the given configuration.<p> 104 * 105 * @param configuration the configuration to use 106 */ 107 public CmsLocalizationWidget(String configuration) { 108 109 super(configuration); 110 } 111 112 /** 113 * @see org.opencms.widgets.I_CmsADEWidget#getConfiguration(org.opencms.file.CmsObject, org.opencms.xml.types.A_CmsXmlContentValue, org.opencms.i18n.CmsMessages, org.opencms.file.CmsResource, java.util.Locale) 114 */ 115 public String getConfiguration( 116 CmsObject cms, 117 A_CmsXmlContentValue schemaType, 118 CmsMessages messages, 119 CmsResource resource, 120 Locale contentLocale) { 121 122 initConfiguration(cms, schemaType); 123 return m_messages.key(m_bundleKey); 124 } 125 126 /** 127 * @see org.opencms.widgets.I_CmsADEWidget#getCssResourceLinks(org.opencms.file.CmsObject) 128 */ 129 public List<String> getCssResourceLinks(CmsObject cms) { 130 131 return null; 132 } 133 134 /** 135 * @see org.opencms.widgets.I_CmsADEWidget#getDefaultDisplayType() 136 */ 137 public DisplayType getDefaultDisplayType() { 138 139 return DisplayType.wide; 140 } 141 142 /** 143 * @see org.opencms.widgets.I_CmsWidget#getDialogWidget(org.opencms.file.CmsObject, org.opencms.widgets.I_CmsWidgetDialog, org.opencms.widgets.I_CmsWidgetParameter) 144 */ 145 public String getDialogWidget(CmsObject cms, I_CmsWidgetDialog widgetDialog, I_CmsWidgetParameter param) { 146 147 String id = param.getId(); 148 // initialize bundle 149 initConfiguration(cms, param); 150 151 StringBuffer result = new StringBuffer(256); 152 153 result.append("<td class=\"xmlTd\">"); 154 result.append("<input class=\"xmlInput textInput"); 155 if (param.hasError()) { 156 result.append(" xmlInputError"); 157 } 158 result.append("\""); 159 result.append(" name=\""); 160 result.append(id); 161 result.append("\" id=\""); 162 result.append(id); 163 result.append("\" value=\""); 164 165 // determine value to show in editor 166 String value = getValue(cms, param); 167 result.append(CmsEncoder.escapeXml(value)); 168 result.append("\">"); 169 result.append("</td>"); 170 171 return result.toString(); 172 } 173 174 /** 175 * @see org.opencms.widgets.I_CmsADEWidget#getInitCall() 176 */ 177 public String getInitCall() { 178 179 return null; 180 } 181 182 /** 183 * @see org.opencms.widgets.I_CmsADEWidget#getJavaScriptResourceLinks(org.opencms.file.CmsObject) 184 */ 185 public List<String> getJavaScriptResourceLinks(CmsObject cms) { 186 187 return null; 188 } 189 190 /** 191 * @see org.opencms.widgets.I_CmsADEWidget#getWidgetName() 192 */ 193 public String getWidgetName() { 194 195 return CmsLocalizationWidget.class.getName(); 196 } 197 198 /** 199 * @see org.opencms.widgets.I_CmsADEWidget#isInternal() 200 */ 201 public boolean isInternal() { 202 203 return true; 204 } 205 206 /** 207 * @see org.opencms.widgets.I_CmsWidget#newInstance() 208 */ 209 public I_CmsWidget newInstance() { 210 211 return new CmsLocalizationWidget(getConfiguration()); 212 } 213 214 /** 215 * @see org.opencms.widgets.I_CmsWidget#setEditorValue(org.opencms.file.CmsObject, java.util.Map, org.opencms.widgets.I_CmsWidgetDialog, org.opencms.widgets.I_CmsWidgetParameter) 216 */ 217 @Override 218 public void setEditorValue( 219 CmsObject cms, 220 Map<String, String[]> formParameters, 221 I_CmsWidgetDialog widgetDialog, 222 I_CmsWidgetParameter param) { 223 224 String[] values = formParameters.get(param.getId()); 225 if ((values != null) && (values.length > 0)) { 226 // initialize bundle 227 initConfiguration(cms, param); 228 String value = m_messages.key(m_bundleKey); 229 if (value.equals(values[0].trim())) { 230 // value is equal to localized value, do not save 231 value = ""; 232 } else { 233 // value is different, save it 234 value = values[0]; 235 // now replace message bundle like argument placeholders like "{0}" with OpenCms macros 236 Matcher matcher = PATTERN_MESSAGEARGUMENT.matcher(value); 237 while (matcher.matches()) { 238 int startIndex = matcher.start(1); 239 int endIndex = matcher.end(3); 240 String number = CmsMacroResolver.formatMacro(matcher.group(2)); 241 // replace arguments with macros 242 value = value.substring(0, startIndex) + number + value.substring(endIndex); 243 matcher = PATTERN_MESSAGEARGUMENT.matcher(value); 244 } 245 } 246 param.setStringValue(cms, value); 247 } 248 } 249 250 /** 251 * Initializes the localized bundle to get the value from, the optional key name and the optional locale.<p> 252 * 253 * @param cms an initialized instance of a CmsObject 254 * @param schemaType the widget parameter to generate the widget for 255 */ 256 protected void initConfiguration(CmsObject cms, A_CmsXmlContentValue schemaType) { 257 258 // set the default bundle key 259 m_bundleKey = schemaType.getName(); 260 // set the default locale for XML contents 261 m_locale = cms.getRequestContext().getLocale(); 262 try { 263 I_CmsXmlContentValue value = schemaType; 264 m_locale = value.getLocale(); 265 } catch (Exception e) { 266 // ignore, this is no XML content 267 } 268 269 // check the message bundle 270 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getConfiguration())) { 271 //initialize messages, the optional bundle key name and the optional locale from configuration String 272 String bundleName = ""; 273 List<String> configs = CmsStringUtil.splitAsList(getConfiguration(), '|'); 274 Iterator<String> i = configs.iterator(); 275 while (i.hasNext()) { 276 String config = i.next(); 277 if (config.startsWith(OPTION_KEY)) { 278 m_bundleKey = config.substring(OPTION_KEY.length()); 279 } else if (config.startsWith(OPTION_LOCALE)) { 280 m_locale = CmsLocaleManager.getLocale(config.substring(OPTION_LOCALE.length())); 281 } else { 282 bundleName = config; 283 } 284 } 285 // create messages object 286 m_messages = new CmsMessages(bundleName, m_locale); 287 } else { 288 // initialize empty messages object to avoid NPE 289 m_messages = new CmsMessages("", m_locale); 290 } 291 } 292 293 /** 294 * Initializes the localized bundle to get the value from, the optional key name and the optional locale.<p> 295 * 296 * @param cms an initialized instance of a CmsObject 297 * @param param the widget parameter to generate the widget for 298 */ 299 protected void initConfiguration(CmsObject cms, I_CmsWidgetParameter param) { 300 301 // set the default bundle key 302 m_bundleKey = param.getName(); 303 // set the default locale for XML contents 304 m_locale = cms.getRequestContext().getLocale(); 305 try { 306 I_CmsXmlContentValue value = (I_CmsXmlContentValue)param; 307 m_locale = value.getLocale(); 308 } catch (Exception e) { 309 // ignore, this is no XML content 310 } 311 312 // check the message bundle 313 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getConfiguration())) { 314 //initialize messages, the optional bundle key name and the optional locale from configuration String 315 String bundleName = ""; 316 List<String> configs = CmsStringUtil.splitAsList(getConfiguration(), '|'); 317 Iterator<String> i = configs.iterator(); 318 while (i.hasNext()) { 319 String config = i.next(); 320 if (config.startsWith(OPTION_KEY)) { 321 m_bundleKey = config.substring(OPTION_KEY.length()); 322 } else if (config.startsWith(OPTION_LOCALE)) { 323 m_locale = CmsLocaleManager.getLocale(config.substring(OPTION_LOCALE.length())); 324 } else { 325 bundleName = config; 326 } 327 } 328 // create messages object 329 m_messages = new CmsMessages(bundleName, m_locale); 330 } else { 331 // initialize empty messages object to avoid NPE 332 m_messages = new CmsMessages("", m_locale); 333 } 334 } 335 336 /** 337 * Determine value to show in editor.<p> 338 * @param cms an initialized instance of a CmsObject 339 * @param param the widget parameter to generate the widget for 340 * 341 * @return value to show in editor 342 */ 343 private String getValue(CmsObject cms, I_CmsWidgetParameter param) { 344 345 String value = m_messages.key(m_bundleKey); 346 if ((CmsStringUtil.isNotEmptyOrWhitespaceOnly(param.getStringValue(cms)) && !value.equals(param.getStringValue(cms))) 347 || value.startsWith(CmsMessages.UNKNOWN_KEY_EXTENSION)) { 348 // saved value is provided and different from localized value in bundle or no value found in bundle, use it 349 value = param.getStringValue(cms); 350 // replace OpenCms macro syntax with message bundle arguments 351 Matcher matcher = PATTERN_MACRO.matcher(value); 352 while (matcher.matches()) { 353 int startIndex = matcher.start(1); 354 int endIndex = matcher.end(3); 355 String number = matcher.group(2); 356 value = value.substring(0, startIndex) + "{" + number + "}" + value.substring(endIndex); 357 matcher = PATTERN_MACRO.matcher(value); 358 } 359 360 } 361 return value; 362 } 363 364}