/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also as published by the Free
* Software Foundation.
*
* This program 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 General Public License and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.rhq.core.gui.configuration;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.el.ValueExpression;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIForm;
import javax.faces.component.UIInput;
import javax.faces.component.UIParameter;
import javax.faces.component.html.HtmlForm;
import javax.faces.component.html.HtmlOutputLink;
import javax.faces.component.html.HtmlPanelGrid;
import javax.faces.component.html.HtmlPanelGroup;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;
import org.ajax4jsf.component.html.HtmlAjaxCommandLink;
import org.ajax4jsf.component.html.HtmlAjaxOutputPanel;
import org.ajax4jsf.component.html.HtmlAjaxRegion;
import org.ajax4jsf.context.AjaxContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.richfaces.component.html.HtmlModalPanel;
import org.richfaces.component.html.HtmlSimpleTogglePanel;
import org.rhq.core.clientapi.agent.configuration.ConfigurationUtility;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertyList;
import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.configuration.definition.PropertyDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionMap;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple;
import org.rhq.core.domain.configuration.definition.PropertyGroupDefinition;
import org.rhq.core.gui.RequestParameterNameConstants;
import org.rhq.core.gui.configuration.helper.PropertyRenderingUtility;
import org.rhq.core.gui.configuration.propset.ConfigurationSetComponent;
import org.rhq.core.gui.configuration.propset.PropertySetComponent;
import org.rhq.core.gui.util.FacesComponentUtility;
import org.rhq.core.gui.util.FacesContextUtility;
import org.rhq.core.gui.util.FacesExpressionUtility;
import org.rhq.core.gui.util.PropertyIdGeneratorUtility;
/**
* A renderer that renders an {@link AbstractConfigurationComponent} component as XHTML.
*
* @author Ian Springer
*/
// TODO: Make subclasses of this for rendering ConfigUIComponents versus rendering ConfigurationSetComponents.
public class ConfigRenderer extends Renderer {
public static final String PROPERTY_SET_COMPONENT_ID = "rhq_propSet";
protected static final String NOTE_PANEL_STYLE_CLASS = "note-panel";
protected static final String PROPERTY_GROUP_HEADER_STYLE_CLASS = "BlockTitle";
protected static final String PROPERTY_GROUP_BODY_STYLE_CLASS = "BlockContent";
protected static final String UNGROUPED_PROPERTIES_STYLE_CLASS = "BlockContent";
protected static final String REQUIRED_MARKER_TEXT_STYLE_CLASS = "required-marker-text";
protected static final String GROUP_DESCRIPTION_PANEL_STYLE_CLASS = "group-description-panel";
protected static final String GROUP_DESCRIPTION_TEXT_PANEL_STYLE_CLASS = "group-description-text-panel";
private final Log LOG = LogFactory.getLog(ConfigRenderer.class);
private static final String INIT_INPUTS_JAVA_SCRIPT_COMPONENT_ID_SUFFIX = "-initInputsJavaScript";
/**
* Decode request parameters for the given {@link ConfigUIComponent}.
*
* @param facesContext the JSF context for the current request
* @param component the {@link ConfigUIComponent} for which request parameters should be decoded
*/
@Override
public void decode(FacesContext facesContext, UIComponent component) {
AbstractConfigurationComponent configurationComponent = (AbstractConfigurationComponent) component;
validateAttributes(configurationComponent);
String function = FacesContextUtility.getOptionalRequestParameter(RequestParameterNameConstants.FUNCTION_PARAM);
if (function != null) {
if (function.equals(AbstractPropertyBagUIComponentTreeFactory.DELETE_LIST_MEMBER_PROPERTY_FUNCTION)) {
deleteListMemberProperty(configurationComponent);
} else if (function
.equals(AbstractPropertyBagUIComponentTreeFactory.DELETE_OPEN_MAP_MEMBER_PROPERTY_FUNCTION)) {
deleteOpenMapMemberProperty(configurationComponent);
}
}
if (configurationComponent.getConfiguration() != null) {
String id = getInitInputsJavaScriptComponentId(configurationComponent);
UIComponent initInputsJavaScriptComponent = configurationComponent.findComponent(id);
if (initInputsJavaScriptComponent != null) {
FacesComponentUtility.detachComponent(initInputsJavaScriptComponent);
PropertyRenderingUtility.addInitInputsJavaScript(configurationComponent, id,
configurationComponent.isFullyEditable(), true);
}
}
}
/**
* Encode the beginning of the given {@link ConfigUIComponent}.
*
* @param facesContext the JSF context for the current request
* @param component the {@link ConfigUIComponent} to be encoded
*/
@Override
public void encodeBegin(FacesContext facesContext, UIComponent component) throws IOException {
AbstractConfigurationComponent configurationComponent = (AbstractConfigurationComponent) component;
if (isAjaxRefresh(configurationComponent)) {
if (configurationComponent instanceof ConfigUIComponent) {
component.getChildren().clear();
} else { // configurationComponent instanceof ConfigurationSetComponent
// If it's an AJAX request for a configSet component, recalculate the group config from the member
// configs and clear our child components. NOTE: This can *not* be done in decode(), because the model
// will not have been updated yet with any changes made to child UIInput values.
ConfigurationSetComponent configurationSetComponent = ((ConfigurationSetComponent) configurationComponent);
HtmlModalPanel propSetModalPanel = configurationSetComponent.getPropSetModalPanel();
if (propertySetComponentContainsInvalidInputs(configurationSetComponent)) {
// Make sure the modal panel is shown when rendered, so the user will see the validation errors.
propSetModalPanel.setShowWhenRendered(true);
} else {
// Otherwise, make sure it's not rendered.
propSetModalPanel.setShowWhenRendered(false);
configurationSetComponent.getConfigurationSet().calculateGroupConfiguration();
component.getChildren().clear();
}
}
}
ResponseWriter writer = facesContext.getResponseWriter();
String clientId = component.getClientId(facesContext);
writer.write('\n');
writer.writeComment("********** Start of " + component.getClass().getSimpleName() + " component **********");
writer.startElement("div", component);
writer.writeAttribute("id", clientId, "clientId");
// Only create the child components the first time around (i.e. once per JSF lifecycle).
if (component.getChildCount() == 0)
addChildComponents(configurationComponent);
}
private boolean isAjaxRefresh(AbstractConfigurationComponent configurationComponent) {
if (configurationComponent instanceof ConfigUIComponent) {
return AjaxContext.getCurrentInstance().isAjaxRequest();
} else { // configurationComponent instanceof ConfigurationSetComponent
String refresh = FacesContextUtility.getOptionalRequestParameter("refresh");
return configurationComponent.getId().equals(refresh);
}
}
private static boolean propertySetComponentContainsInvalidInputs(ConfigurationSetComponent configurationSetComponent) {
HtmlModalPanel propSetModalPanel = configurationSetComponent.getPropSetModalPanel();
boolean containsInvalidInputs = false;
List<PropertySetComponent> propertySetComponents = FacesComponentUtility.getDescendantsOfType(
propSetModalPanel, PropertySetComponent.class);
if (propertySetComponents.size() == 1) {
PropertySetComponent propertySetComponent = propertySetComponents.get(0);
List<UIInput> inputs = FacesComponentUtility.getDescendantsOfType(propertySetComponent, UIInput.class);
for (UIInput input : inputs) {
if (!input.isValid()) {
containsInvalidInputs = true;
break;
}
}
}
return containsInvalidInputs;
}
@Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
ResponseWriter writer = context.getResponseWriter();
writer.writeText("\n", component, null);
writer.endElement("div");
writer.writeComment("********** End of " + component.getClass().getSimpleName() + " component **********");
}
public void addChildComponents(AbstractConfigurationComponent configurationComponent) {
if ((configurationComponent.getConfigurationDefinition() == null)
|| ((configurationComponent.getConfiguration() != null) && configurationComponent.getConfiguration()
.getMap().isEmpty())) {
if (configurationComponent.getNullConfigurationDefinitionMessage() != null) {
String styleClass = (configurationComponent.getNullConfigurationStyle() == null) ? "ErrorBlock"
: configurationComponent.getNullConfigurationStyle();
HtmlPanelGroup messagePanel = FacesComponentUtility.addBlockPanel(configurationComponent,
configurationComponent, styleClass);
FacesComponentUtility.addVerbatimText(messagePanel,
configurationComponent.getNullConfigurationDefinitionMessage());
}
return;
}
if (configurationComponent.getConfiguration() == null) {
if (configurationComponent.getNullConfigurationMessage() != null) {
HtmlPanelGroup messagePanel = FacesComponentUtility.addBlockPanel(configurationComponent,
configurationComponent, "WarnBlock");
FacesComponentUtility.addVerbatimText(messagePanel,
configurationComponent.getNullConfigurationMessage());
}
return;
}
ConfigurationUtility.normalizeConfiguration(configurationComponent.getConfiguration(),
configurationComponent.getConfigurationDefinition());
if (configurationComponent instanceof ConfigurationSetComponent) {
ConfigurationSetComponent configurationSetComponent = (ConfigurationSetComponent) configurationComponent;
// Only add the propSet modal panel if it's not already in the component tree.
if (configurationSetComponent.getPropSetModalPanel() == null)
addPropSetModalPanel(configurationSetComponent);
// Otherwise, if the propSet was just submitted and it doesn't contain any validation errors,
// close the propSet modal.
else if (isAjaxRefresh(configurationSetComponent)
&& !propertySetComponentContainsInvalidInputs(configurationSetComponent)) {
HtmlModalPanel propSetModalPanel = configurationSetComponent.getPropSetModalPanel();
String script = "Richfaces.hideModalPanel('"
+ propSetModalPanel.getClientId(FacesContext.getCurrentInstance()) + "');";
FacesComponentUtility.addJavaScript(configurationSetComponent, null, null, script);
}
}
if (!configurationComponent.isReadOnly())
addRequiredNotationsKey(configurationComponent);
if (configurationComponent.getListName() != null) {
if (configurationComponent.getListIndex() == null) {
// No index specified means we should add a new map to the list.
configurationComponent.setListIndex(addNewMap(configurationComponent));
}
addListMemberProperty(configurationComponent);
} else {
addConfiguration(configurationComponent);
}
String id = getInitInputsJavaScriptComponentId(configurationComponent);
PropertyRenderingUtility.addInitInputsJavaScript(configurationComponent, id,
configurationComponent.isFullyEditable(), false);
}
private void addListMemberProperty(AbstractConfigurationComponent configurationComponent) {
// Update member Configurations if this is a group config.
if (configurationComponent instanceof ConfigurationSetComponent) {
ConfigurationSetComponent configurationSetComponent = ((ConfigurationSetComponent) configurationComponent);
configurationSetComponent.getConfigurationSet().applyGroupConfiguration();
}
// Now add a new component to the JSF component tree.
AbstractPropertyBagUIComponentTreeFactory propertyListUIComponentTreeFactory = new MapInListUIComponentTreeFactory(
configurationComponent, configurationComponent.getListName(), configurationComponent.getListIndex());
HtmlPanelGroup propertiesPanel = FacesComponentUtility.addBlockPanel(configurationComponent,
configurationComponent, UNGROUPED_PROPERTIES_STYLE_CLASS);
propertiesPanel.getChildren().add(propertyListUIComponentTreeFactory.createUIComponentTree(null));
}
private void deleteListMemberProperty(AbstractConfigurationComponent configurationComponent) {
String listName = FacesContextUtility
.getRequiredRequestParameter(RequestParameterNameConstants.LIST_NAME_PARAM);
PropertyList propertyList = configurationComponent.getConfiguration().getList(listName);
if (propertyList == null) {
FacesContextUtility.addMessage(FacesMessage.SEVERITY_ERROR, "Configuration does not contain a list named '"
+ listName + "'.");
return;
}
int listIndex = FacesContextUtility.getRequiredRequestParameter(RequestParameterNameConstants.LIST_INDEX_PARAM,
Integer.class);
if (listIndex >= propertyList.getList().size()) {
FacesContextUtility.addMessage(FacesMessage.SEVERITY_ERROR, "List '" + listName
+ "' does not contain an item at index " + listIndex + ".");
return;
}
// First remove the corresponding JSF component from the component tree.
Property memberProperty = propertyList.getList().get(listIndex);
String memberComponentId = PropertyIdGeneratorUtility.getIdentifier(memberProperty, listIndex,
AbstractPropertyBagUIComponentTreeFactory.PANEL_ID_SUFFIX);
UIComponent listMemberComponent = configurationComponent.findComponent(memberComponentId);
if (listMemberComponent == null) {
throw new IllegalStateException("JSF component with id '" + memberComponentId + "' not found for list '"
+ listName + "' item " + listIndex + ".");
}
FacesComponentUtility.detachComponent(listMemberComponent);
correctListIndexesInSiblingMembers(configurationComponent, propertyList, listIndex);
// Now delete the property from the actual Configuration.
propertyList.getList().remove(listIndex);
// ...and from member Configurations if this is a group config.
if (configurationComponent instanceof ConfigurationSetComponent) {
ConfigurationSetComponent configurationSetComponent = ((ConfigurationSetComponent) configurationComponent);
configurationSetComponent.getConfigurationSet().applyGroupConfiguration();
}
FacesContextUtility.addMessage(FacesMessage.SEVERITY_INFO, "Property at index " + listIndex
+ " deleted from list '" + listName + "'.");
}
private void correctListIndexesInSiblingMembers(AbstractConfigurationComponent config, PropertyList propertyList,
int deletedMemberIndex) {
for (int oldIndex = deletedMemberIndex + 1; oldIndex < propertyList.getList().size(); oldIndex++) {
int newIndex = oldIndex - 1;
String listMemberComponentId = PropertyIdGeneratorUtility.getIdentifier(propertyList.getList()
.get(oldIndex), oldIndex, AbstractPropertyBagUIComponentTreeFactory.PANEL_ID_SUFFIX);
UIComponent listMemberComponent = config.findComponent(listMemberComponentId);
String newListMemberComponentId = PropertyIdGeneratorUtility.getIdentifier(
propertyList.getList().get(newIndex), newIndex,
AbstractPropertyBagUIComponentTreeFactory.PANEL_ID_SUFFIX);
listMemberComponent.setId(newListMemberComponentId);
String listIndexParamComponentId = PropertyIdGeneratorUtility.getIdentifier(
propertyList.getList().get(oldIndex), oldIndex,
AbstractPropertyBagUIComponentTreeFactory.PARAM_ID_SUFFIX);
UIParameter listIndexParam = (UIParameter) config.findComponent(listIndexParamComponentId);
String newListIndexParamComponentId = PropertyIdGeneratorUtility.getIdentifier(
propertyList.getList().get(newIndex), newIndex,
AbstractPropertyBagUIComponentTreeFactory.PARAM_ID_SUFFIX);
listIndexParam.setId(newListIndexParamComponentId);
listIndexParam.setValue(newIndex);
}
}
private void deleteOpenMapMemberProperty(AbstractConfigurationComponent configurationComponent) {
String mapName = FacesContextUtility.getRequiredRequestParameter(RequestParameterNameConstants.MAP_NAME_PARAM);
PropertyMap propertyMap = configurationComponent.getConfiguration().getMap(mapName);
if (propertyMap == null) {
FacesContextUtility.addMessage(FacesMessage.SEVERITY_ERROR, "Configuration does not contain a map named '"
+ mapName + "'.");
return;
}
String memberName = FacesContextUtility
.getRequiredRequestParameter(RequestParameterNameConstants.MEMBER_NAME_PARAM);
if (!propertyMap.getMap().containsKey(memberName)) {
FacesContextUtility.addMessage(FacesMessage.SEVERITY_ERROR, "Map '" + mapName
+ "' does not contain a member property named '" + memberName + "'.");
return;
}
Property memberProperty = propertyMap.getMap().get(memberName);
String memberComponentId = PropertyIdGeneratorUtility.getIdentifier(memberProperty, null,
AbstractPropertyBagUIComponentTreeFactory.PANEL_ID_SUFFIX);
UIComponent mapMemberComponent = configurationComponent.findComponent(memberComponentId);
if (mapMemberComponent == null) {
throw new IllegalStateException("JSF component with id '" + memberComponentId + "' not found for map '"
+ mapName + "' member property '" + memberName + "'.");
}
FacesComponentUtility.detachComponent(mapMemberComponent);
// Now delete the property from the actual Configuration.
propertyMap.getMap().remove(memberName);
// ...and from member Configurations if this is a group config.
if (configurationComponent instanceof ConfigurationSetComponent) {
ConfigurationSetComponent configurationSetComponent = ((ConfigurationSetComponent) configurationComponent);
configurationSetComponent.getConfigurationSet().applyGroupConfiguration();
}
FacesContextUtility.addMessage(FacesMessage.SEVERITY_INFO, "Property '" + memberName + "' deleted from map '"
+ mapName + "'.");
}
private void addRequiredNotationsKey(AbstractConfigurationComponent config) {
addDebug(config, true, ".addNotePanel()");
HtmlPanelGroup footnotesPanel = FacesComponentUtility.addBlockPanel(config, config, NOTE_PANEL_STYLE_CLASS);
FacesComponentUtility.addOutputText(footnotesPanel, config, "*", REQUIRED_MARKER_TEXT_STYLE_CLASS);
FacesComponentUtility.addOutputText(footnotesPanel, config, " denotes a required field.",
FacesComponentUtility.NO_STYLE_CLASS);
if (config.isGroup()) {
HtmlPanelGroup overridePanel = FacesComponentUtility.addBlockPanel(config, config, NOTE_PANEL_STYLE_CLASS);
FacesComponentUtility.addOutputText(overridePanel, config,
"note: if override is not checked, that property will not be altered on any group members",
FacesComponentUtility.NO_STYLE_CLASS);
}
addDebug(config, false, ".addNotePanel()");
}
private void addConfiguration(AbstractConfigurationComponent config) {
addNonGroupedProperties(config);
addGroupedProperties(config);
}
private void addNonGroupedProperties(AbstractConfigurationComponent config) {
addDebug(config, true, ".addNonGroupedProperties()");
HtmlPanelGroup propertiesPanel = FacesComponentUtility.addBlockPanel(config, config,
UNGROUPED_PROPERTIES_STYLE_CLASS);
AbstractPropertyBagUIComponentTreeFactory propertyListUIComponentTreeFactory = new GroupUIComponentTreeFactory(
config, GroupUIComponentTreeFactory.NO_GROUP);
propertiesPanel.getChildren().add(propertyListUIComponentTreeFactory.createUIComponentTree(null));
addDebug(config, false, ".addNonGroupedProperties()");
}
private void addGroupedProperties(AbstractConfigurationComponent config) {
addDebug(config, true, ".addGroupedProperties()");
List<PropertyGroupDefinition> groups = config.getConfigurationDefinition().getGroupDefinitions();
for (PropertyGroupDefinition group : groups) {
HtmlSimpleTogglePanel groupPanel = addGroupPanel(config, group);
AbstractPropertyBagUIComponentTreeFactory propertyListUIComponentTreeFactory = new GroupUIComponentTreeFactory(
config, group.getName());
groupPanel.getChildren().add(propertyListUIComponentTreeFactory.createUIComponentTree(null));
}
addDebug(config, false, ".addGroupedProperties()");
}
private HtmlSimpleTogglePanel addGroupPanel(AbstractConfigurationComponent config, PropertyGroupDefinition group) {
addDebug(config, true, ".addGroupPanel()");
HtmlSimpleTogglePanel groupPanel = FacesComponentUtility.addSimpleTogglePanel(config, config, null);
// TODO: On AJAX requests, set "opened" attribute to its previous state.
groupPanel.setOpened(!group.isDefaultHidden());
groupPanel.setHeaderClass(PROPERTY_GROUP_HEADER_STYLE_CLASS);
groupPanel.setBodyClass(PROPERTY_GROUP_BODY_STYLE_CLASS);
// Custom header that includes the name and description
HtmlPanelGroup headerPanel = FacesComponentUtility.createBlockPanel(config, null);
FacesComponentUtility.addOutputText(headerPanel, config, group.getDisplayName(), null);
FacesComponentUtility
.addOutputText(headerPanel, config, group.getDescription(), "group-description-text-panel");
groupPanel.getFacets().put("header", headerPanel);
// custom "close" widget
HtmlPanelGroup closePanel = FacesComponentUtility.createBlockPanel(config, null);
closePanel.setStyle("text-align: right; font-weight: normal; font-size: 0.8em; whitespace: nowrap;");
FacesComponentUtility.addGraphicImage(closePanel, config, "/images/ico_trigger_wht_collapse.gif", "collapse");
FacesComponentUtility.addOutputText(closePanel, config, " Collapse", null);
groupPanel.getFacets().put("closeMarker", closePanel);
// custom "open" widget
HtmlPanelGroup openPanel = FacesComponentUtility.createBlockPanel(config, null);
openPanel.setStyle("text-align: right; font-weight: normal; font-size: 0.8em; whitespace: nowrap;");
FacesComponentUtility.addGraphicImage(openPanel, config, "/images/ico_trigger_wht_expand.gif", "expand");
FacesComponentUtility.addOutputText(openPanel, config, " Expand", null);
groupPanel.getFacets().put("openMarker", openPanel);
addDebug(config, true, ".addGroupPanel()");
return groupPanel;
}
private void validateAttributes(AbstractConfigurationComponent configurationComponent) {
// TODO: Add back attribute validation - remember config and configSet components require different attributes.
/*if (configurationComponent.getValueExpression("configurationDefinition") == null) {
throw new IllegalStateException("The " + configurationComponent.getClass().getName()
+ " component requires a 'configurationDefinition' attribute.");
}
if (configurationComponent.getValueExpression("configuration") == null) {
throw new IllegalStateException("The " + configurationComponent.getClass().getName()
+ " component requires a 'configuration' attribute.");
}*/
}
private int addNewMap(AbstractConfigurationComponent config) {
String listName = config.getListName();
PropertyDefinitionMap mapDefinition = (PropertyDefinitionMap) config.getConfigurationDefinition()
.getPropertyDefinitionList(listName).getMemberDefinition();
String mapName = mapDefinition.getName();
PropertyMap newMap = new PropertyMap(mapName);
for (PropertyDefinition mapMemberDefinition : mapDefinition.getOrderedPropertyDefinitions()) {
PropertyDefinitionSimple simpleDefinition = (PropertyDefinitionSimple) mapMemberDefinition;
newMap.put(new PropertySimple(simpleDefinition.getName(), (simpleDefinition.isRequired()) ? "" : null));
}
PropertyList list = config.getConfiguration().getList(listName);
list.add(newMap);
return list.getList().size() - 1;
}
/**
* simple method that will add debugging HTML comments so that I can figure out which part of the HTML is generated
* by which part of this class
*
* @param component the component this comment should be added
* @param start true if this is the "START" comment, false if it is the "END" comment
* @param methodName the name of the method this is calling from
*/
private void addDebug(UIComponent component, boolean start, String methodName) {
if (LOG.isDebugEnabled()) {
StringBuilder msg = new StringBuilder("\n<!--");
msg.append(start ? " START " : " END ");
msg.append(this.getClass().getSimpleName());
msg.append(methodName);
msg.append(" -->\n ");
FacesComponentUtility.addVerbatimText(component, msg);
}
}
private String getInitInputsJavaScriptComponentId(AbstractConfigurationComponent configUIComponent) {
return configUIComponent.getId() + INIT_INPUTS_JAVA_SCRIPT_COMPONENT_ID_SUFFIX;
}
private void addPropSetModalPanel(ConfigurationSetComponent configurationSetComponent) {
UIForm configSetForm = FacesComponentUtility.getEnclosingForm(configurationSetComponent);
// Add the modal panel as a sibling of the configSet form.
HtmlModalPanel modalPanel = FacesComponentUtility.createComponent(HtmlModalPanel.class);
String modalPanelId = ConfigurationSetComponent.getPropSetModalPanelId(configurationSetComponent);
modalPanel.setId(modalPanelId);
modalPanel.setWidth(715);
modalPanel.setHeight(535);
modalPanel.setTrimOverlayedElements(false);
modalPanel.setStyle("overflow-y: auto;");
modalPanel.setOnbeforeshow("sizeAppropriately('configSetForm:" + modalPanelId + "')");
modalPanel.setOnresize("keepCentered('configSetForm:" + modalPanelId + "')");
// Insert the modal panel as a sibling of the configSet form, just after the form in the component tree.
int index = getComponentIndex(configSetForm);
configSetForm.getParent().getChildren().add(index + 1, modalPanel);
// Add the propSet form as a child of the modal panel.
HtmlForm propSetForm = new HtmlForm();
modalPanel.getChildren().add(propSetForm);
String propSetFormId = ConfigurationSetComponent.getPropSetFormId(configurationSetComponent);
propSetForm.setId(propSetFormId);
propSetForm.setOnsubmit("prepareInputsForSubmission(this)");
// Add inputs for the request params that need to be forwarded when the propSet form is submitted.
forwardParameter(propSetForm, "id");
forwardParameter(propSetForm, "groupId");
forwardParameter(propSetForm, "arcuId");
forwardParameter(propSetForm, "apcuId");
forwardParameter(propSetForm, "mode");
addPropSetButtons(configurationSetComponent, propSetForm);
// Add an AJAX region+panel as a child of this second form.
HtmlAjaxRegion ajaxRegion = FacesComponentUtility.createComponent(HtmlAjaxRegion.class);
propSetForm.getChildren().add(ajaxRegion);
HtmlAjaxOutputPanel ajaxOutputPanel = FacesComponentUtility.createComponent(HtmlAjaxOutputPanel.class);
ajaxRegion.getChildren().add(ajaxOutputPanel);
ajaxOutputPanel.setLayout("block");
ajaxOutputPanel.setAjaxRendered(true);
ajaxOutputPanel.setKeepTransient(true);
// And finally add the propSet component as a child of this AJAX region.
PropertySetComponent propertySet = new PropertySetComponent();
ajaxOutputPanel.getChildren().add(propertySet);
propertySet.setId(PROPERTY_SET_COMPONENT_ID);
propertySet.setReadOnly(configurationSetComponent.isReadOnly());
propertySet.setListIndex(configurationSetComponent.getListIndex());
ValueExpression valueExpression = FacesExpressionUtility.createValueExpression(
"#{param.propertyExpressionString}", String.class);
propertySet.setValueExpression(PropertySetComponent.PROPERTY_EXPRESSION_STRING_ATTRIBUTE, valueExpression);
// The below can be uncommented in order for the "propertyExpressionValue" attribute to have a valid value
// on the initial page load (i.e. for testing purposes).
//propertySet.getAttributes().put(PropertySetComponent.PROPERTY_EXPRESSION_STRING_ATTRIBUTE,
// "#{EditTestConfigurationUIBean.configurationSet.groupConfiguration.map['String1'].stringValue}");
propertySet.setValueExpression(PropertySetComponent.CONFIGURATION_SET_ATTRIBUTE,
configurationSetComponent.getValueExpression(ConfigurationSetComponent.CONFIGURATION_SET_ATTRIBUTE));
addPropSetButtons(configurationSetComponent, propSetForm);
return;
}
private void forwardParameter(HtmlForm modalPanelForm, String paramName) {
Map<String, String> requestParamMap = FacesContext.getCurrentInstance().getExternalContext()
.getRequestParameterMap();
String paramValue = requestParamMap.get(paramName);
if (paramValue != null)
FacesComponentUtility.addVerbatimText(modalPanelForm, "<input type='hidden' name='" + paramName
+ "' value='" + paramValue + "'/>");
}
private void addPropSetButtons(ConfigurationSetComponent configurationSetComponent, HtmlForm modalPanelForm) {
//the modal panel form is embedded inside the modal panel itself
String modalPanelClientId = modalPanelForm.getParent().getClientId(FacesContext.getCurrentInstance());
HtmlPanelGrid panelGrid = FacesComponentUtility.addPanelGrid(modalPanelForm, null, 2,
CssStyleClasses.BUTTONS_TABLE);
// OK button
HtmlAjaxCommandLink okLink = FacesComponentUtility.createComponent(HtmlAjaxCommandLink.class);
panelGrid.getChildren().add(okLink);
okLink.setTitle("OK");
FacesComponentUtility.addParameter(okLink, null, "refresh", configurationSetComponent.getId());
FacesComponentUtility.addButton(okLink, "OK", CssStyleClasses.BUTTON_MEDIUM);
// Cancel button (only if not read-only)
if (!configurationSetComponent.isReadOnly()) {
HtmlOutputLink cancelLink = FacesComponentUtility.addOutputLink(panelGrid, null, "#");
cancelLink.setOnclick("Richfaces.hideModalPanel('" + modalPanelClientId + "'); return false;");
cancelLink.setTitle("Cancel");
FacesComponentUtility.addButton(cancelLink, "CANCEL", CssStyleClasses.BUTTON_MEDIUM);
}
}
private static int getComponentIndex(UIForm component) {
if (component.getParent() == null)
return -1;
List<UIComponent> children = component.getParent().getChildren();
for (int i = 0; i < children.size(); i++) {
if (children.get(i) == component)
return i;
}
throw new IllegalStateException("Unable to determine index of component " + component);
}
}