org.opencms.gwt.client.property.CmsVfsModePropertyEditor Maven / Gradle / Ivy
Show all versions of opencms-gwt Show documentation
/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* For further information about Alkacon Software, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.gwt.client.property;
import org.opencms.gwt.client.Messages;
import org.opencms.gwt.client.ui.CmsPopup;
import org.opencms.gwt.client.ui.CmsScrollPanel;
import org.opencms.gwt.client.ui.input.CmsTextBox;
import org.opencms.gwt.client.ui.input.I_CmsFormField;
import org.opencms.gwt.client.ui.input.I_CmsFormWidget;
import org.opencms.gwt.client.ui.input.I_CmsHasGhostValue;
import org.opencms.gwt.client.ui.input.I_CmsStringModel;
import org.opencms.gwt.client.ui.input.form.CmsBasicFormField;
import org.opencms.gwt.client.util.CmsDebugLog;
import org.opencms.gwt.client.util.CmsDomUtil;
import org.opencms.gwt.client.util.CmsDomUtil.Style;
import org.opencms.gwt.shared.CmsListInfoBean;
import org.opencms.gwt.shared.property.CmsClientProperty;
import org.opencms.gwt.shared.property.CmsClientProperty.Mode;
import org.opencms.gwt.shared.property.CmsPathValue;
import org.opencms.util.CmsPair;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;
import org.opencms.xml.content.CmsXmlContentProperty;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.HasFocusHandlers;
import com.google.gwt.event.logical.shared.BeforeSelectionEvent;
import com.google.gwt.event.logical.shared.BeforeSelectionHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.user.client.ui.Widget;
/**
* The sitemap entry editor class for the VFS mode.
*
* @since 8.0.0
*/
public class CmsVfsModePropertyEditor extends A_CmsPropertyEditor {
/** The interval used for updating the height. */
public static final int UPDATE_HEIGHT_INTERVAL = 200;
protected static boolean m_resizeDisabled;
/** The map of tab names. */
private static BiMap tabs;
/** The map of models of the fields. */
Map m_models = new HashMap();
/** Active field data. */
private CmsActiveFieldData m_activeFieldData;
/** Field data which should be restored. */
private CmsActiveFieldData m_fieldDataToBeRestored;
/** Flag to control whether the properties should be editable. */
private boolean m_isReadOnly;
/** The previous tab index. */
private int m_oldTabIndex = -1;
/** The panel used for editing the properties. */
private CmsPropertyPanel m_panel;
/** The properties of the entry. */
private Map m_properties;
/** Flag which indicates whether the resource properties should be editable. */
private boolean m_showResourceProperties;
/**
* Creates a new sitemap entry editor instance for the VFS mode.
*
* @param propConfig the property configuration
* @param handler the sitemap entry editor handler
*/
public CmsVfsModePropertyEditor(Map propConfig, I_CmsPropertyEditorHandler handler) {
super(propConfig, handler);
m_properties = CmsClientProperty.makeLazyCopy(handler.getOwnProperties());
}
static {
tabs = HashBiMap.create();
tabs.put(Mode.effective, CmsPropertyPanel.TAB_SIMPLE);
tabs.put(Mode.structure, CmsPropertyPanel.TAB_INDIVIDUAL);
tabs.put(Mode.resource, CmsPropertyPanel.TAB_SHARED);
}
/**
* Disables resizing.
*
* @param disabled true if resizing should be disabled
*/
public static void disableResize(boolean disabled) {
m_resizeDisabled = disabled;
}
/**
* @see org.opencms.gwt.client.property.A_CmsPropertyEditor#buildFields()
*/
@Override
public void buildFields() {
internalBuildConfiguredFields();
internalBuildFields(Mode.structure);
if (m_showResourceProperties) {
internalBuildFields(Mode.resource);
}
}
/**
* @see org.opencms.gwt.client.property.A_CmsPropertyEditor#createFormWidget(java.lang.String, java.util.Map)
*/
@Override
public I_CmsFormWidget createFormWidget(String key, Map widgetParams) {
I_CmsFormWidget widget = super.createFormWidget(key, widgetParams);
if (m_isReadOnly) {
widget.setEnabled(false);
}
return widget;
}
/**
* Gets the active field data.
*
* @return the active field data
*/
public CmsActiveFieldData getActiveFieldData() {
return m_activeFieldData;
}
/**
* Handles field value changes.
*
* @param field the changed field
*/
public void handleFieldChange(I_CmsFormField field) {
if ((m_activeFieldData != null) && (m_activeFieldData.getField() != field)) {
m_activeFieldData = null;
}
}
/**
* @see org.opencms.gwt.client.property.A_CmsPropertyEditor#initializeWidgets(org.opencms.gwt.client.ui.CmsPopup)
*/
@Override
public void initializeWidgets(final CmsPopup dialog) {
super.initializeWidgets(dialog);
dialog.setCaption(null);
dialog.removePadding();
m_panel.tryToRestoreFieldData(m_fieldDataToBeRestored);
Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
public boolean execute() {
if (!getPropertyPanel().getTabPanel().isAttached() || !getPropertyPanel().getTabPanel().isVisible()) {
return false;
}
if (!m_resizeDisabled) {
updateHeight(dialog);
}
return true;
}
}, UPDATE_HEIGHT_INTERVAL);
}
/**
* Sets the active field data to be restored.
*
* @param fieldData the active field data to be restored
*/
public void restoreActiveFieldData(CmsActiveFieldData fieldData) {
m_fieldDataToBeRestored = fieldData;
}
/**
* Sets the "readonly" mode.
*
* @param readonly if true, readonly mode will be enabled
*/
public void setReadOnly(boolean readonly) {
m_isReadOnly = readonly;
}
/**
* Sets the "show resource properties" flag which controls whether the resource value fields should be built.
*
* @param showResourceProperties if true, the resource value fields will be build
*/
public void setShowResourceProperties(boolean showResourceProperties) {
m_showResourceProperties = showResourceProperties;
}
/**
* Returns the property panel.
*
* @return the property panel
*/
protected CmsPropertyPanel getPropertyPanel() {
return m_panel;
}
/**
* Method which is called when the tab is switched.
*
* @param toTab the tab to which the user is switching
*/
protected void handleSwitchTab(int toTab) {
switch (toTab) {
case 0:
rebuildSimpleTab();
break;
case 1:
rebuildIndividualTab();
break;
case 2:
rebuildSharedTab();
break;
default:
break;
}
if (m_disabledReason != null) {
disableInput(m_disabledReason);
} else {
m_form.validateAllFields();
}
}
/**
* @see org.opencms.gwt.client.property.A_CmsPropertyEditor#setupFieldContainer()
*/
@Override
protected void setupFieldContainer() {
CmsListInfoBean info = m_handler.getPageInfo();
m_panel = new CmsPropertyPanel(m_showResourceProperties, info);
String modeClass = m_handler.getModeClass();
if (modeClass != null) {
m_panel.addStyleName(modeClass);
}
m_panel.addBeforeSelectionHandler(new BeforeSelectionHandler() {
public void onBeforeSelection(BeforeSelectionEvent event) {
int target = event.getItem().intValue();
handleSwitchTab(target);
}
});
m_form.setWidget(m_panel);
}
/**
* Updates the panel height depending on the content of the current tab.
*
* @param dialog the dialog for which the height should be updated
*/
protected void updateHeight(CmsPopup dialog) {
int tabIndex = m_panel.getTabPanel().getSelectedIndex();
boolean changedTab = tabIndex != m_oldTabIndex;
m_oldTabIndex = tabIndex;
CmsScrollPanel tabWidget = m_panel.getTabPanel().getWidget(tabIndex);
Element innerElement = tabWidget.getWidget().getElement();
int contentHeight = CmsDomUtil.getCurrentStyleInt(innerElement, Style.height);
int spaceLeft = dialog.getAvailableHeight(0);
int newHeight = Math.min(spaceLeft, contentHeight + 47);
boolean changedHeight = m_panel.getTabPanel().getOffsetHeight() != newHeight;
if (changedHeight || changedTab) {
m_panel.getTabPanel().setHeight(newHeight + "px");
int selectedIndex = m_panel.getTabPanel().getSelectedIndex();
CmsScrollPanel widget = m_panel.getTabPanel().getWidget(selectedIndex);
widget.setHeight((newHeight - 34) + "px");
widget.onResizeDescendant();
//dialog.center();
}
}
/**
* Builds a single form field.
*
* @param ownProps the entry's own properties
* @param propName the property name
* @param mode the mode which controls which kind of field will be built
*/
private void buildField(
Map ownProps,
final String propName,
CmsClientProperty.Mode mode) {
String entryId = m_handler.getId().toString();
CmsXmlContentProperty propDef = m_propertyConfig.get(propName);
if (propDef == null) {
String widget = CmsClientProperty.PROPERTY_TEMPLATE.equals(propName) ? "template" : "string";
propDef = new CmsXmlContentProperty(
propName,
"string",
widget,
"",
null,
null,
null,
null,
null,
null,
null);
}
if (mode != Mode.effective) {
propDef = propDef.withNiceName(propName);
}
CmsClientProperty ownProp = m_properties.get(propName);
CmsPathValue pathValue = CmsClientProperty.getPathValue(ownProp, mode).prepend(entryId + "/" + propName + "/");
//CHECK: should fields other than NavText be really automatically allowed to be empty in navigation mode?
final String tab = tabs.get(mode);
final CmsBasicFormField field = CmsBasicFormField.createField(
propDef,
pathValue.getPath() + "#" + tab,
this,
Collections. emptyMap(),
true);
CmsPair defaultValueAndOrigin = getDefaultValueToDisplay(ownProp, mode);
String defaultValue = "";
String origin = "";
if (defaultValueAndOrigin != null) {
defaultValue = defaultValueAndOrigin.getFirst();
origin = defaultValueAndOrigin.getSecond();
}
Widget w = (Widget)field.getWidget();
I_CmsStringModel model = getStringModel(pathValue);
field.bind(model);
boolean ghost = CmsStringUtil.isEmptyOrWhitespaceOnly(pathValue.getValue());
String initialValue = pathValue.getValue();
if (w instanceof I_CmsHasGhostValue) {
((I_CmsHasGhostValue)w).setGhostValue(defaultValue, ghost);
if (ghost) {
initialValue = null;
}
}
if ((w instanceof CmsTextBox)) {
try {
((HasFocusHandlers)w).addFocusHandler(new FocusHandler() {
@SuppressWarnings("synthetic-access")
public void onFocus(FocusEvent event) {
m_activeFieldData = new CmsActiveFieldData(field, tab, propName);
}
});
} catch (Exception e) {
CmsDebugLog.consoleLog("" + e);
}
}
boolean isShowingGhost = ghost && !CmsStringUtil.isEmpty(defaultValue);
if (isShowingGhost) {
field.getLayoutData().put("info", origin);
}
if (!ghost || isShowingGhost) {
field.getLayoutData().put(CmsPropertyPanel.LD_DISPLAY_VALUE, "true");
}
field.getLayoutData().put(CmsPropertyPanel.LD_PROPERTY, propName);
field.getLayoutData().put("tab", tab);
m_form.addField(tab, field, initialValue);
}
/**
* Creates a string model which uses a field of a CmsClientProperty for storing its value.
*
* @param id the structure id
* @param propName the property id
* @param isStructure if true, the structure value field should be used, else the resource value field
*
*
* @return the new model object
*/
private I_CmsStringModel createStringModel(final CmsUUID id, final String propName, final boolean isStructure) {
final CmsClientProperty property = m_properties.get(propName);
return new I_CmsStringModel() {
private boolean m_active;
private EventBus m_eventBus = new SimpleEventBus();
public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) {
return m_eventBus.addHandler(ValueChangeEvent.getType(), handler);
}
/**
* @see com.google.gwt.event.shared.HasHandlers#fireEvent(com.google.gwt.event.shared.GwtEvent)
*/
public void fireEvent(GwtEvent> event) {
m_eventBus.fireEvent(event);
}
public String getId() {
return Joiner.on("/").join(id.toString(), propName, isStructure ? "S" : "R");
}
public String getValue() {
if (isStructure) {
return property.getStructureValue();
} else {
return property.getResourceValue();
}
}
public void setValue(String value, boolean notify) {
if (!m_active) {
m_active = true;
try {
String oldValue = getValue();
boolean changed = !Objects.equal(value, oldValue);
if (isStructure) {
property.setStructureValue(value);
} else {
property.setResourceValue(value);
}
if (notify && changed) {
ValueChangeEvent.fire(this, value);
}
} finally {
m_active = false;
}
}
}
};
}
/**
* Gets a pair of strings containing the default value to display for a given property and its source.
*
* @param prop the property
* @param mode the mode
*
* @return a pair of the form (defaultValue, origin)
*/
private CmsPair getDefaultValueToDisplay(CmsClientProperty prop, Mode mode) {
if ((mode == Mode.structure) && !CmsStringUtil.isEmpty(prop.getResourceValue())) {
String message = Messages.get().key(Messages.GUI_ORIGIN_SHARED_0);
return CmsPair.create(prop.getResourceValue(), message);
}
CmsClientProperty inheritedProperty = m_handler.getInheritedProperty(prop.getName());
if (CmsClientProperty.isPropertyEmpty(inheritedProperty)) {
return null;
}
CmsPathValue pathValue = inheritedProperty.getPathValue(mode);
String message = Messages.get().key(Messages.GUI_ORIGIN_INHERITED_1, inheritedProperty.getOrigin());
return CmsPair.create(pathValue.getValue(), message);
}
/**
* Creates a string model for a given property path value, and returns the same model if the same path value is passed in.
*
* @param pathValue the path value
*
* @return the model for that path value
*/
private I_CmsStringModel getStringModel(CmsPathValue pathValue) {
String path = pathValue.getPath();
I_CmsStringModel model = m_models.get(path);
if (model == null) {
String[] tokens = path.split("/");
String id = tokens[0];
String propName = tokens[1];
boolean isStructure = tokens[2].equals("S");
model = createStringModel(new CmsUUID(id), propName, isStructure);
m_models.put(path, model);
}
return model;
}
/**
* Builds the fields for the configured properties in the first tab.
*/
private void internalBuildConfiguredFields() {
Map ownProps = m_handler.getOwnProperties();
List keys = new ArrayList(m_propertyConfig.keySet());
moveToTop(keys, CmsClientProperty.PROPERTY_NAVTEXT);
moveToTop(keys, CmsClientProperty.PROPERTY_DESCRIPTION);
moveToTop(keys, CmsClientProperty.PROPERTY_TITLE);
for (String propName : keys) {
buildField(ownProps, propName, Mode.effective);
}
}
/**
*
* Builds the fields for a given mode.
*
* @param mode the mode
*/
private void internalBuildFields(Mode mode) {
Map ownProps = m_handler.getOwnProperties();
for (String propName : m_handler.getAllPropertyNames()) {
buildField(ownProps, propName, mode);
}
}
/**
* Moves the given property name to the top of the keys if present.
*
* @param keys the list of keys
* @param propertyName the property name to move
*/
private void moveToTop(List keys, String propertyName) {
if (keys.contains(propertyName)) {
keys.remove(propertyName);
keys.add(0, propertyName);
}
}
/**
* Rebuilds the "individual" tab.
*/
private void rebuildIndividualTab() {
m_form.removeGroup(CmsPropertyPanel.TAB_INDIVIDUAL);
CmsPropertyPanel panel = ((CmsPropertyPanel)m_form.getWidget());
panel.clearTab(CmsPropertyPanel.TAB_INDIVIDUAL);
internalBuildFields(Mode.structure);
m_form.renderGroup(CmsPropertyPanel.TAB_INDIVIDUAL);
}
/**
* Rebuilds the "shared" tab.
*/
private void rebuildSharedTab() {
m_form.removeGroup(CmsPropertyPanel.TAB_SHARED);
CmsPropertyPanel panel = ((CmsPropertyPanel)m_form.getWidget());
panel.clearTab(CmsPropertyPanel.TAB_SHARED);
internalBuildFields(Mode.resource);
m_form.renderGroup(CmsPropertyPanel.TAB_SHARED);
}
/**
* Rebuilds the simple tab.
*/
private void rebuildSimpleTab() {
m_form.removeGroup(CmsPropertyPanel.TAB_SIMPLE);
CmsPropertyPanel panel = ((CmsPropertyPanel)m_form.getWidget());
panel.clearTab(CmsPropertyPanel.TAB_SIMPLE);
if (m_handler.hasEditableName()) {
m_form.addField(CmsPropertyPanel.TAB_SIMPLE, createUrlNameField());
}
internalBuildConfiguredFields();
m_form.renderGroup(CmsPropertyPanel.TAB_SIMPLE);
}
}