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.gwt.client.property; 029 030import org.opencms.file.CmsResource; 031import org.opencms.gwt.client.Messages; 032import org.opencms.gwt.client.ui.CmsNotification; 033import org.opencms.gwt.client.ui.CmsNotification.Type; 034import org.opencms.gwt.client.ui.CmsPopup; 035import org.opencms.gwt.client.ui.input.CmsDefaultStringModel; 036import org.opencms.gwt.client.ui.input.CmsSelectBox; 037import org.opencms.gwt.client.ui.input.CmsTextBox; 038import org.opencms.gwt.client.ui.input.I_CmsFormField; 039import org.opencms.gwt.client.ui.input.I_CmsFormWidget; 040import org.opencms.gwt.client.ui.input.I_CmsHasGhostValue; 041import org.opencms.gwt.client.ui.input.I_CmsStringModel; 042import org.opencms.gwt.client.ui.input.form.CmsBasicFormField; 043import org.opencms.gwt.client.ui.input.form.CmsForm; 044import org.opencms.gwt.client.ui.input.form.CmsWidgetFactoryRegistry; 045import org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetMultiFactory; 046import org.opencms.gwt.client.ui.input.tinymce.CmsTinyMCEWidget; 047import org.opencms.gwt.shared.property.CmsClientTemplateBean; 048import org.opencms.util.CmsUUID; 049import org.opencms.xml.content.CmsXmlContentProperty; 050 051import java.util.LinkedHashMap; 052import java.util.List; 053import java.util.Map; 054 055import com.google.common.base.Optional; 056import com.google.gwt.event.logical.shared.HasValueChangeHandlers; 057 058/** 059 * The abstract base class for dialogs to edit properties.<p> 060 * 061 * @since 8.0.0 062 */ 063public abstract class A_CmsPropertyEditor implements I_CmsFormWidgetMultiFactory { 064 065 /** The field id for the link selector widget. */ 066 public static final String FIELD_LINK = "field_link"; 067 068 /** The field id of the "url name" form field. */ 069 public static final String FIELD_URLNAME = "field_urlname"; 070 071 /** The list of all property names. */ 072 protected List<String> m_allProps; 073 074 /** The reason to disable the form input fields. */ 075 protected String m_disabledReason; 076 077 /** True if only the name edit field is disabled. */ 078 protected boolean m_nameOnlyDisabled; 079 080 /** The form containing the fields. */ 081 protected CmsForm m_form; 082 083 /** The handler for this sitemap entry editor. */ 084 protected I_CmsPropertyEditorHandler m_handler; 085 086 /** The configuration of the properties. */ 087 protected Map<String, CmsXmlContentProperty> m_propertyConfig; 088 089 /** The URL name field. */ 090 protected I_CmsFormField m_urlNameField; 091 092 /** The model for the URL name field. */ 093 protected CmsDefaultStringModel m_urlNameModel; 094 095 /** True if the 'disabled' notification has already been sent. */ 096 private boolean m_disabledNotificationSent; 097 098 /** 099 * Creates a new sitemap entry editor.<p> 100 * 101 * @param handler the handler 102 * @param propertyConfig the property configuration 103 */ 104 public A_CmsPropertyEditor( 105 Map<String, CmsXmlContentProperty> propertyConfig, 106 final I_CmsPropertyEditorHandler handler) { 107 108 CmsForm form = new CmsForm(null); 109 m_form = form; 110 m_handler = handler; 111 m_propertyConfig = removeHiddenProperties(propertyConfig); 112 113 } 114 115 /** 116 * Checks whether a widget can be used in the sitemap entry editor, and throws an exception otherwise.<p> 117 * 118 * @param key the widget key 119 * @param widget the created widget 120 */ 121 public static void checkWidgetRequirements(String key, I_CmsFormWidget widget) { 122 123 if (widget instanceof CmsTinyMCEWidget) { 124 return; 125 } 126 if (!((widget instanceof I_CmsHasGhostValue) && (widget instanceof HasValueChangeHandlers<?>))) { 127 throw new CmsWidgetNotSupportedException(key); 128 } 129 } 130 131 /** 132 * @see org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetMultiFactory#createFormWidget(java.lang.String, java.util.Map, com.google.common.base.Optional) 133 */ 134 public I_CmsFormWidget createFormWidget( 135 String key, 136 Map<String, String> widgetParams, 137 Optional<String> defaultValue) { 138 139 I_CmsFormWidget result = null; 140 141 if ("template".equals(key)) { 142 result = createTemplateSelector(); 143 } else if (CmsTextBox.WIDGET_TYPE.equals(key)) { 144 CmsTextBox textBox = new CmsTextBox().colorWhite(); 145 textBox.setErrorMessageWidth("345px"); 146 textBox.setTriggerChangeOnKeyPress(true); 147 // we need this because the tab containing the text box may not be visible 148 // at the time the error message is set, so measuring the field's size would 149 // yield an invalid value 150 result = textBox; 151 } else if (CmsSelectBox.WIDGET_TYPE.equals(key)) { 152 final CmsPropertySelectBox box = new CmsPropertySelectBox(widgetParams); 153 result = box; 154 155 } else { 156 result = CmsWidgetFactoryRegistry.instance().createFormWidget(key, widgetParams, defaultValue); 157 checkWidgetRequirements(key, result); 158 } 159 return result; 160 } 161 162 /** 163 * Disables all input to the form.<p> 164 * 165 * @param disabledReason the reason to display to the user 166 * @param nameOnlyDisabled true if only the name editing field is disabled 167 */ 168 public void disableInput(String disabledReason, boolean nameOnlyDisabled) { 169 170 m_disabledReason = disabledReason; 171 m_nameOnlyDisabled = nameOnlyDisabled; 172 if (!nameOnlyDisabled) { 173 for (I_CmsFormField field : m_form.getFields().values()) { 174 field.getWidget().setEnabled(false); 175 } 176 } 177 m_urlNameField.getWidget().setEnabled(false); 178 if (!m_disabledNotificationSent && (m_disabledReason != null)) { 179 CmsNotification.get().send(Type.WARNING, m_disabledReason); 180 m_disabledNotificationSent = true; 181 } 182 } 183 184 /** 185 * Gets the form for the properties.<p> 186 * 187 * @return the property form 188 */ 189 public CmsForm getForm() { 190 191 return m_form; 192 } 193 194 /** 195 * Initializes the widgets for editing the properties.<p> 196 * 197 * @param dialog the dialog which the property editor is part of 198 */ 199 public void initializeWidgets(CmsPopup dialog) { 200 201 // creates tabs, etc. if necessary 202 setupFieldContainer(); 203 addSpecialFields(); 204 // create fields and add them to the correct location 205 buildFields(); 206 m_form.setValidatorClass("org.opencms.gwt.CmsDefaultFormValidator"); 207 m_form.render(); 208 } 209 210 /** 211 * Sets the names of properties which can be edited.<p> 212 * 213 * @param propertyNames the property names 214 */ 215 public void setPropertyNames(List<String> propertyNames) { 216 217 m_allProps = propertyNames; 218 } 219 220 /** 221 * Method to add special, non-property fields.<p> 222 */ 223 protected void addSpecialFields() { 224 225 String firstTab = m_form.getWidget().getDefaultGroup(); 226 if (m_handler.hasEditableName()) { 227 // the root entry name can't be edited 228 CmsBasicFormField urlNameField = createUrlNameField(); 229 m_form.addField(firstTab, urlNameField); 230 } 231 } 232 233 /** 234 * Builds and renders the fields for the properties.<p> 235 */ 236 protected abstract void buildFields(); 237 238 /** 239 * Creates the text field for editing the URL name.<p> 240 * 241 * @return the newly created form field 242 */ 243 protected CmsBasicFormField createUrlNameField() { 244 245 if (m_urlNameField != null) { 246 m_urlNameField.unbind(); 247 } 248 249 String description = message(Messages.GUI_URLNAME_PROPERTY_DESC_0); 250 String label = message(Messages.GUI_URLNAME_PROPERTY_0); 251 final CmsTextBox textbox = new CmsTextBox(); 252 textbox.setTriggerChangeOnKeyPress(true); 253 textbox.setInhibitValidationForKeypresses(true); 254 255 CmsBasicFormField result = new CmsBasicFormField(FIELD_URLNAME, description, label, null, textbox); 256 result.getLayoutData().put("property", A_CmsPropertyEditor.FIELD_URLNAME); 257 String urlName = m_handler.getName(); 258 if (urlName == null) { 259 urlName = ""; 260 } 261 String parent = CmsResource.getParentFolder(m_handler.getPath()); 262 CmsUUID id = m_handler.getId(); 263 264 result.setValidator(new CmsUrlNameValidator(parent, id)); 265 I_CmsStringModel model = getUrlNameModel(urlName); 266 result.getWidget().setFormValueAsString(model.getValue()); 267 result.bind(model); 268 //result.getWidget().setFormValueAsString(getUrlNameModel().getValue()); 269 m_urlNameField = result; 270 return result; 271 } 272 273 /** 274 * Gets the title from a map of field values.<p> 275 * 276 * @param fieldValues the map of field values 277 * @return the title 278 */ 279 protected String getTitle(Map<String, String> fieldValues) { 280 281 for (Map.Entry<String, String> entry : fieldValues.entrySet()) { 282 if (entry.getKey().contains("/NavText/")) { 283 return entry.getValue(); 284 } 285 } 286 return null; 287 } 288 289 /** 290 * Lazily creates the model object for the URL name field.<p> 291 * 292 * @param urlName the initial value for the URL name 293 * 294 * @return the model object for the URL name field 295 */ 296 protected CmsDefaultStringModel getUrlNameModel(String urlName) { 297 298 if (m_urlNameModel == null) { 299 m_urlNameModel = new CmsDefaultStringModel("urlname"); 300 m_urlNameModel.setValue(urlName, false); 301 } 302 return m_urlNameModel; 303 } 304 305 /** 306 * Returns a localized message from the message bundle.<p> 307 * 308 * @param key the message key 309 * @param args the message parameters 310 * 311 * @return the localized message 312 */ 313 protected String message(String key, Object... args) { 314 315 return Messages.get().key(key, args); 316 } 317 318 /** 319 * Sets the ghost value for a form field if its normal value is empty and the field's widget supports ghost values.<p> 320 * 321 * @param field the form field 322 * @param value the ghost value to set 323 * @param ghostMode if true, sets the widget to ghost mode 324 */ 325 protected void setGhostValue(I_CmsFormField field, String value, boolean ghostMode) { 326 327 I_CmsFormWidget widget = field.getWidget(); 328 if ((widget instanceof I_CmsHasGhostValue) && (value != null)) { 329 ((I_CmsHasGhostValue)widget).setGhostValue(value, ghostMode); 330 } 331 } 332 333 /** 334 * Sets up the widget which will contain the input fields for the properties.<p> 335 */ 336 protected abstract void setupFieldContainer(); 337 338 /** 339 * Sets the contents of the URL name field in the form.<p> 340 * 341 * @param urlName the new URL name 342 */ 343 protected void setUrlNameField(String urlName) { 344 345 m_form.getField(FIELD_URLNAME).getWidget().setFormValueAsString(urlName); 346 } 347 348 /** 349 * Shows an error message next to the URL name input field.<p> 350 * 351 * @param message the message which should be displayed, or null if no message should be displayed 352 */ 353 protected void showUrlNameError(String message) { 354 355 m_form.getField(FIELD_URLNAME).getWidget().setErrorMessage(message); 356 } 357 358 /** 359 * Helper method for creating the template selection widget.<p> 360 * 361 * @return the template selector widget 362 */ 363 private I_CmsFormWidget createTemplateSelector() { 364 365 if (m_handler.useAdeTemplates()) { 366 367 CmsSelectBox selectBox = null; 368 Map<String, String> values = new LinkedHashMap<String, String>(); 369 for (Map.Entry<String, CmsClientTemplateBean> templateEntry : m_handler.getPossibleTemplates().entrySet()) { 370 CmsClientTemplateBean template = templateEntry.getValue(); 371 String title = template.getTitle(); 372 if ((title == null) || (title.length() == 0)) { 373 title = template.getSitePath(); 374 } 375 values.put(template.getSitePath(), title); 376 } 377 selectBox = new CmsPropertySelectBox(values); 378 return selectBox; 379 } else { 380 CmsTextBox textbox = new CmsTextBox(); 381 return textbox; 382 } 383 } 384 385 /** 386 * Helper method for removing hidden properties from a map of property configurations.<p> 387 * 388 * The map passed into the method is not changed; a map which only contains the non-hidden 389 * property definitions is returned.<p> 390 * 391 * @param propConfig the property configuration 392 * 393 * @return the filtered property configuration 394 */ 395 private Map<String, CmsXmlContentProperty> removeHiddenProperties(Map<String, CmsXmlContentProperty> propConfig) { 396 397 Map<String, CmsXmlContentProperty> result = new LinkedHashMap<String, CmsXmlContentProperty>(); 398 for (Map.Entry<String, CmsXmlContentProperty> entry : propConfig.entrySet()) { 399 if (!m_handler.isHiddenProperty(entry.getKey())) { 400 result.put(entry.getKey(), entry.getValue()); 401 } 402 } 403 return result; 404 } 405}