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.gwt.client.Messages; 031import org.opencms.gwt.client.ui.input.I_CmsHasGhostValue; 032import org.opencms.gwt.client.ui.input.I_CmsStringModel; 033import org.opencms.gwt.client.ui.input.form.CmsBasicFormField; 034import org.opencms.gwt.client.ui.input.form.CmsSimpleFormFieldPanel; 035import org.opencms.gwt.shared.property.CmsClientProperty; 036import org.opencms.gwt.shared.property.CmsClientProperty.Mode; 037import org.opencms.gwt.shared.property.CmsPathValue; 038import org.opencms.gwt.shared.property.CmsPropertyModification; 039import org.opencms.util.CmsPair; 040import org.opencms.util.CmsStringUtil; 041import org.opencms.util.CmsUUID; 042import org.opencms.xml.content.CmsXmlContentProperty; 043 044import java.util.ArrayList; 045import java.util.Collections; 046import java.util.HashMap; 047import java.util.List; 048import java.util.Map; 049 050import com.google.common.base.Joiner; 051import com.google.common.base.Objects; 052import com.google.gwt.event.logical.shared.ValueChangeEvent; 053import com.google.gwt.event.logical.shared.ValueChangeHandler; 054import com.google.gwt.event.shared.EventBus; 055import com.google.gwt.event.shared.GwtEvent; 056import com.google.gwt.event.shared.HandlerRegistration; 057import com.google.gwt.event.shared.SimpleEventBus; 058import com.google.gwt.user.client.ui.Widget; 059 060/** 061 * The sitemap entry editor class for the VFS mode.<p> 062 * 063 * @since 8.0.0 064 */ 065public class CmsSimplePropertyEditor extends A_CmsPropertyEditor { 066 067 /** The map of models of the fields. */ 068 Map<String, I_CmsStringModel> m_models = new HashMap<String, I_CmsStringModel>(); 069 070 /** The properties of the entry. */ 071 private Map<String, CmsClientProperty> m_properties; 072 073 /** 074 * Creates a new sitemap entry editor instance for the VFS mode.<p> 075 * 076 * @param propConfig the property configuration 077 * @param handler the sitemap entry editor handler 078 */ 079 public CmsSimplePropertyEditor(Map<String, CmsXmlContentProperty> propConfig, I_CmsPropertyEditorHandler handler) { 080 081 super(propConfig, handler); 082 m_properties = CmsClientProperty.makeLazyCopy(handler.getOwnProperties()); 083 } 084 085 /** 086 * @see org.opencms.gwt.client.property.A_CmsPropertyEditor#buildFields() 087 */ 088 @Override 089 public void buildFields() { 090 091 Map<String, CmsClientProperty> ownProps = m_handler.getOwnProperties(); 092 Map<String, CmsClientProperty> defaultFileProps = m_handler.getDefaultFileProperties(); 093 Map<String, CmsClientProperty> props; 094 CmsUUID id = null; 095 if (!m_handler.isFolder()) { 096 props = ownProps; 097 id = m_handler.getId(); 098 } else if (m_handler.getDefaultFileId() != null) { 099 props = defaultFileProps; 100 id = m_handler.getDefaultFileId(); 101 } else { 102 props = ownProps; 103 id = m_handler.getId(); 104 } 105 props = CmsClientProperty.makeLazyCopy(props); 106 List<String> keys = new ArrayList<String>(m_propertyConfig.keySet()); 107 moveToTop(keys, CmsClientProperty.PROPERTY_NAVTEXT); 108 moveToTop(keys, CmsClientProperty.PROPERTY_DESCRIPTION); 109 moveToTop(keys, CmsClientProperty.PROPERTY_TITLE); 110 moveToTop(keys, CmsPropertyModification.FILE_NAME_PROPERTY); 111 for (String propName : keys) { 112 buildField(props, propName, Mode.effective, id); 113 } 114 } 115 116 /** 117 * @see org.opencms.gwt.client.property.A_CmsPropertyEditor#addSpecialFields() 118 */ 119 @Override 120 protected void addSpecialFields() { 121 122 // we don't want any special fields 123 } 124 125 /** 126 * Checks whether an empty string should always be allowed for the property, regardless of validation settings. 127 * 128 * @param name the property name 129 * @return true if the empty string should always be allowed 130 */ 131 protected boolean isAlwaysAllowEmpty(String name) { 132 133 return true; 134 } 135 136 /** 137 * @see org.opencms.gwt.client.property.A_CmsPropertyEditor#setupFieldContainer() 138 */ 139 @Override 140 protected void setupFieldContainer() { 141 142 CmsSimpleFormFieldPanel panel = new CmsSimpleFormFieldPanel(); 143 m_form.setWidget(panel); 144 } 145 146 /** 147 * Builds a single form field.<p> 148 * 149 * @param ownProps the entry's own properties 150 * @param propName the property name 151 * @param mode the mode which controls which kind of field will be built 152 * @param id the id of the resource for which to build the field 153 */ 154 private void buildField( 155 Map<String, CmsClientProperty> ownProps, 156 final String propName, 157 CmsClientProperty.Mode mode, 158 CmsUUID id) { 159 160 CmsXmlContentProperty propDef = m_propertyConfig.get(propName); 161 162 if (propDef == null) { 163 String widget = CmsClientProperty.PROPERTY_TEMPLATE.equals(propName) ? "template" : "string"; 164 propDef = new CmsXmlContentProperty( 165 propName, 166 "string", 167 widget, 168 "", 169 null, 170 null, 171 null, 172 null, 173 null, 174 null, 175 null); 176 } 177 178 if (mode != Mode.effective) { 179 propDef = propDef.withNiceName(propName); 180 } 181 182 CmsClientProperty ownProp = ownProps.get(propName); 183 CmsPathValue pathValue = CmsClientProperty.getPathValue(ownProp, mode).prepend(id + "/" + propName + "/"); 184 185 //CHECK: should fields other than NavText be really automatically allowed to be empty in navigation mode? 186 CmsBasicFormField field = CmsBasicFormField.createField( 187 propDef, 188 pathValue.getPath(), 189 this, 190 Collections.<String, String> emptyMap(), 191 isAlwaysAllowEmpty(propDef.getName())); 192 193 CmsPair<String, String> defaultValueAndOrigin = getDefaultValueToDisplay(ownProp, mode); 194 String defaultValue = ""; 195 String origin = ""; 196 if (defaultValueAndOrigin != null) { 197 defaultValue = defaultValueAndOrigin.getFirst(); 198 origin = defaultValueAndOrigin.getSecond(); 199 } 200 Widget w = (Widget)field.getWidget(); 201 I_CmsStringModel model = getStringModel(pathValue); 202 field.bind(model); 203 boolean ghost = CmsStringUtil.isEmptyOrWhitespaceOnly(pathValue.getValue()); 204 String initialValue = pathValue.getValue(); 205 if (w instanceof I_CmsHasGhostValue) { 206 ((I_CmsHasGhostValue)w).setGhostValue(defaultValue, ghost); 207 if (ghost) { 208 initialValue = null; 209 } 210 } 211 212 boolean isShowingGhost = ghost && !CmsStringUtil.isEmpty(defaultValue); 213 214 if (isShowingGhost) { 215 field.getLayoutData().put("info", origin); 216 } 217 if (!ghost || isShowingGhost) { 218 field.getLayoutData().put(CmsPropertyPanel.LD_DISPLAY_VALUE, "true"); 219 } 220 field.getLayoutData().put(CmsPropertyPanel.LD_PROPERTY, propName); 221 m_form.addField(field, initialValue); 222 } 223 224 /** 225 * Creates a string model which uses a field of a CmsClientProperty for storing its value.<p> 226 * 227 * @param id the structure id 228 * @param propName the property id 229 * @param isStructure if true, the structure value field should be used, else the resource value field 230 * 231 * 232 * @return the new model object 233 */ 234 private I_CmsStringModel createStringModel(final CmsUUID id, final String propName, final boolean isStructure) { 235 236 final CmsClientProperty property = m_properties.get(propName); 237 238 return new I_CmsStringModel() { 239 240 private boolean m_active; 241 242 private EventBus m_eventBus = new SimpleEventBus(); 243 244 public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) { 245 246 return m_eventBus.addHandler(ValueChangeEvent.getType(), handler); 247 } 248 249 /** 250 * @see com.google.gwt.event.shared.HasHandlers#fireEvent(com.google.gwt.event.shared.GwtEvent) 251 */ 252 public void fireEvent(GwtEvent<?> event) { 253 254 m_eventBus.fireEvent(event); 255 } 256 257 public String getId() { 258 259 return Joiner.on("/").join(id.toString(), propName, isStructure ? "S" : "R"); 260 } 261 262 public String getValue() { 263 264 if (isStructure) { 265 return property.getStructureValue(); 266 } else { 267 return property.getResourceValue(); 268 } 269 } 270 271 public void setValue(String value, boolean notify) { 272 273 if (!m_active) { 274 m_active = true; 275 try { 276 String oldValue = getValue(); 277 boolean changed = !Objects.equal(value, oldValue); 278 if (isStructure) { 279 property.setStructureValue(value); 280 } else { 281 property.setResourceValue(value); 282 } 283 if (notify && changed) { 284 ValueChangeEvent.fire(this, value); 285 } 286 } finally { 287 m_active = false; 288 } 289 } 290 } 291 }; 292 } 293 294 /** 295 * Gets a pair of strings containing the default value to display for a given property and its source.<p> 296 * 297 * @param prop the property 298 * @param mode the mode 299 * 300 * @return a pair of the form (defaultValue, origin) 301 */ 302 private CmsPair<String, String> getDefaultValueToDisplay(CmsClientProperty prop, Mode mode) { 303 304 if ((mode == Mode.structure) && !CmsStringUtil.isEmpty(prop.getResourceValue())) { 305 String message = Messages.get().key(Messages.GUI_ORIGIN_SHARED_0); 306 307 return CmsPair.create(prop.getResourceValue(), message); 308 } 309 CmsClientProperty inheritedProperty = m_handler.getInheritedProperty(prop.getName()); 310 if (CmsClientProperty.isPropertyEmpty(inheritedProperty)) { 311 return null; 312 } 313 CmsPathValue pathValue = inheritedProperty.getPathValue(mode); 314 String message = Messages.get().key(Messages.GUI_ORIGIN_INHERITED_1, inheritedProperty.getOrigin()); 315 return CmsPair.create(pathValue.getValue(), message); 316 } 317 318 /** 319 * Creates a string model for a given property path value, and returns the same model if the same path value is passed in.<p> 320 * 321 * @param pathValue the path value 322 * 323 * @return the model for that path value 324 */ 325 private I_CmsStringModel getStringModel(CmsPathValue pathValue) { 326 327 String path = pathValue.getPath(); 328 I_CmsStringModel model = m_models.get(path); 329 if (model == null) { 330 String[] tokens = path.split("/"); 331 String id = tokens[0]; 332 String propName = tokens[1]; 333 boolean isStructure = tokens[2].equals("S"); 334 model = createStringModel(new CmsUUID(id), propName, isStructure); 335 m_models.put(path, model); 336 } 337 return model; 338 } 339 340 /** 341 * Moves the given property name to the top of the keys if present.<p> 342 * 343 * @param keys the list of keys 344 * @param propertyName the property name to move 345 */ 346 private void moveToTop(List<String> keys, String propertyName) { 347 348 if (keys.contains(propertyName)) { 349 keys.remove(propertyName); 350 keys.add(0, propertyName); 351 } 352 } 353 354}