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 if ((dialog != null) && (dialog.getWidth() > 20)) { 209 getForm().getWidget().truncate("property_editing", dialog.getWidth() - 20); 210 } 211 } 212 213 /** 214 * Sets the names of properties which can be edited.<p> 215 * 216 * @param propertyNames the property names 217 */ 218 public void setPropertyNames(List<String> propertyNames) { 219 220 m_allProps = propertyNames; 221 } 222 223 /** 224 * Method to add special, non-property fields.<p> 225 */ 226 protected void addSpecialFields() { 227 228 String firstTab = m_form.getWidget().getDefaultGroup(); 229 if (m_handler.hasEditableName()) { 230 // the root entry name can't be edited 231 CmsBasicFormField urlNameField = createUrlNameField(); 232 m_form.addField(firstTab, urlNameField); 233 } 234 } 235 236 /** 237 * Builds and renders the fields for the properties.<p> 238 */ 239 protected abstract void buildFields(); 240 241 /** 242 * Creates the text field for editing the URL name.<p> 243 * 244 * @return the newly created form field 245 */ 246 protected CmsBasicFormField createUrlNameField() { 247 248 if (m_urlNameField != null) { 249 m_urlNameField.unbind(); 250 } 251 252 String description = message(Messages.GUI_URLNAME_PROPERTY_DESC_0); 253 String label = message(Messages.GUI_URLNAME_PROPERTY_0); 254 final CmsTextBox textbox = new CmsTextBox(); 255 textbox.setTriggerChangeOnKeyPress(true); 256 textbox.setInhibitValidationForKeypresses(true); 257 258 CmsBasicFormField result = new CmsBasicFormField(FIELD_URLNAME, description, label, null, textbox); 259 result.getLayoutData().put("property", A_CmsPropertyEditor.FIELD_URLNAME); 260 String urlName = m_handler.getName(); 261 if (urlName == null) { 262 urlName = ""; 263 } 264 String parent = CmsResource.getParentFolder(m_handler.getPath()); 265 CmsUUID id = m_handler.getId(); 266 267 result.setValidator(new CmsUrlNameValidator(parent, id)); 268 I_CmsStringModel model = getUrlNameModel(urlName); 269 result.getWidget().setFormValueAsString(model.getValue()); 270 result.bind(model); 271 //result.getWidget().setFormValueAsString(getUrlNameModel().getValue()); 272 m_urlNameField = result; 273 return result; 274 } 275 276 /** 277 * Gets the title from a map of field values.<p> 278 * 279 * @param fieldValues the map of field values 280 * @return the title 281 */ 282 protected String getTitle(Map<String, String> fieldValues) { 283 284 for (Map.Entry<String, String> entry : fieldValues.entrySet()) { 285 if (entry.getKey().contains("/NavText/")) { 286 return entry.getValue(); 287 } 288 } 289 return null; 290 } 291 292 /** 293 * Lazily creates the model object for the URL name field.<p> 294 * 295 * @param urlName the initial value for the URL name 296 * 297 * @return the model object for the URL name field 298 */ 299 protected CmsDefaultStringModel getUrlNameModel(String urlName) { 300 301 if (m_urlNameModel == null) { 302 m_urlNameModel = new CmsDefaultStringModel("urlname"); 303 m_urlNameModel.setValue(urlName, false); 304 } 305 return m_urlNameModel; 306 } 307 308 /** 309 * Returns a localized message from the message bundle.<p> 310 * 311 * @param key the message key 312 * @param args the message parameters 313 * 314 * @return the localized message 315 */ 316 protected String message(String key, Object... args) { 317 318 return Messages.get().key(key, args); 319 } 320 321 /** 322 * Sets the ghost value for a form field if its normal value is empty and the field's widget supports ghost values.<p> 323 * 324 * @param field the form field 325 * @param value the ghost value to set 326 * @param ghostMode if true, sets the widget to ghost mode 327 */ 328 protected void setGhostValue(I_CmsFormField field, String value, boolean ghostMode) { 329 330 I_CmsFormWidget widget = field.getWidget(); 331 if ((widget instanceof I_CmsHasGhostValue) && (value != null)) { 332 ((I_CmsHasGhostValue)widget).setGhostValue(value, ghostMode); 333 } 334 } 335 336 /** 337 * Sets up the widget which will contain the input fields for the properties.<p> 338 */ 339 protected abstract void setupFieldContainer(); 340 341 /** 342 * Sets the contents of the URL name field in the form.<p> 343 * 344 * @param urlName the new URL name 345 */ 346 protected void setUrlNameField(String urlName) { 347 348 m_form.getField(FIELD_URLNAME).getWidget().setFormValueAsString(urlName); 349 } 350 351 /** 352 * Shows an error message next to the URL name input field.<p> 353 * 354 * @param message the message which should be displayed, or null if no message should be displayed 355 */ 356 protected void showUrlNameError(String message) { 357 358 m_form.getField(FIELD_URLNAME).getWidget().setErrorMessage(message); 359 } 360 361 /** 362 * Helper method for creating the template selection widget.<p> 363 * 364 * @return the template selector widget 365 */ 366 private I_CmsFormWidget createTemplateSelector() { 367 368 if (m_handler.useAdeTemplates()) { 369 370 CmsSelectBox selectBox = null; 371 Map<String, String> values = new LinkedHashMap<String, String>(); 372 for (Map.Entry<String, CmsClientTemplateBean> templateEntry : m_handler.getPossibleTemplates().entrySet()) { 373 CmsClientTemplateBean template = templateEntry.getValue(); 374 String title = template.getTitle(); 375 if ((title == null) || (title.length() == 0)) { 376 title = template.getSitePath(); 377 } 378 values.put(template.getSitePath(), title); 379 } 380 selectBox = new CmsPropertySelectBox(values); 381 return selectBox; 382 } else { 383 CmsTextBox textbox = new CmsTextBox(); 384 return textbox; 385 } 386 } 387 388 /** 389 * Helper method for removing hidden properties from a map of property configurations.<p> 390 * 391 * The map passed into the method is not changed; a map which only contains the non-hidden 392 * property definitions is returned.<p> 393 * 394 * @param propConfig the property configuration 395 * 396 * @return the filtered property configuration 397 */ 398 private Map<String, CmsXmlContentProperty> removeHiddenProperties(Map<String, CmsXmlContentProperty> propConfig) { 399 400 Map<String, CmsXmlContentProperty> result = new LinkedHashMap<String, CmsXmlContentProperty>(); 401 for (Map.Entry<String, CmsXmlContentProperty> entry : propConfig.entrySet()) { 402 if (!m_handler.isHiddenProperty(entry.getKey())) { 403 result.put(entry.getKey(), entry.getValue()); 404 } 405 } 406 return result; 407 } 408}