*
@@ -148,7 +153,7 @@
html.append(name);
html.append("'");
html.append(" style='display: block; width: 20px; height: 20px;'>");
- if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(iconPath)) {
+ if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(iconPath) && hasFileExtension(iconPath)) {
html.append("
+ * This Parameter can also be seperated by "/" if a ChoiceContentDefinition has Elements who are also ChoiceContentDefinitions.
+ *
+ * @return the name of the Choice element to add
+ */
+ public String getParamChoiceElementName() {
+
+ return m_paramChoiceElementName;
+ }
+
+ /**
* Returns the parameter that specifies the model file name.
*
* @return the parameter that specifies the model file name
@@ -1024,6 +1058,17 @@
}
/**
+ * Sets the name of the Choice-Element to add.
+ * This Parameter can also be seperated by "/" if a ChoiceContentDefinition has Elements who are also ChoiceContentDefinitions.
+ *
+ * @param elementName the name of the Choice-Element to add
+ */
+ public void setParamChoiceElementName(String elementName) {
+
+ m_paramChoiceElementName = elementName;
+ }
+
+ /**
* Sets the parameter that specifies the model file name.
*
* @param paramMasterFile the parameter that specifies the model file name
@@ -1321,9 +1366,12 @@
* @param index the index of the element
* @param addElement if true, the button to add an element is shown
* @param removeElement if true, the button to remove an element is shown
+ * @param choiceListJson List of Choices if a ChoiceContentDefinition should be added.
+ * This must be a valid Javascript-JSON-String
+ * Example value for choiceListJson: [{Label:"first test", Name:"test1", Help:"The ElementType can be a Simple or Complex Type"},{Label:"second test", Name:"test2"}]
* @return the html for the element operation buttons
*/
- private String buildElementButtons(String elementName, int index, boolean addElement, boolean removeElement) {
+ private String buildElementButtons(String elementName, int index, boolean addElement, boolean removeElement, String choiceListJson) {
StringBuffer jsCall = new StringBuffer(512);
@@ -1369,9 +1417,13 @@
// build the add element button if required
if (addElement) {
jsCall.append(Boolean.TRUE);
+ // This param must be Javascript & HTML escaped.
+ // It is the Parameter of the showElementButtons()-function who create a HTML-link with href="javascript:addElement(... choiceListJson ...)"
+ jsCall.append(", \"" + CmsStringUtil.escapeJavaScript(CmsStringUtil.escapeHtml(choiceListJson)) + "\"");
buttonPresent = true;
} else {
jsCall.append(Boolean.FALSE);
+ jsCall.append(", \"[]\"");
}
jsCall.append(");");
@@ -1391,6 +1443,7 @@
action.append(elementName);
action.append("', ");
action.append(index);
+ action.append(", " + choiceListJson + "");
action.append(");");
btAction = action.toString();
} else if (removeElement) {
@@ -1406,9 +1459,9 @@
}
StringBuffer href = new StringBuffer(512);
href.append("javascript:");
- href.append(btAction);
+ href.append(CmsStringUtil.escapeHtml(btAction)); //escape quotes in HTML-Attribute
href.append("\" onmouseover=\"");
- href.append(jsCall);
+ href.append(CmsStringUtil.escapeHtml(jsCall.toString())); //escape quotes in HTML-Attribute
href.append("checkElementButtons(true);\" onmouseout=\"checkElementButtons(false);\" id=\"btimg.");
href.append(elementName).append(".").append(index);
result = button(href.toString(), null, btIcon, Messages.GUI_EDITOR_XMLCONTENT_ELEMENT_BUTTONS_0, 0);
@@ -1635,7 +1688,7 @@
for (Iterator i = contentDefinition.getTypeSequence().iterator(); i.hasNext();) {
// get the type
I_CmsXmlSchemaType type = (I_CmsXmlSchemaType)i.next();
-
+
boolean tabCurrentlyOpened = false;
if (useTabs) {
@@ -1686,11 +1739,28 @@
firstElement = false;
}
+
+ // get nestedContent Infos (also Choice-Content)
CmsXmlContentDefinition nestedContentDefinition = contentDefinition;
+ List choiceList = new ArrayList(); //needed for Javascript-ADD-Button
+
if (!type.isSimpleType()) {
// get nested content definition for nested types
CmsXmlNestedContentDefinition nestedSchema = (CmsXmlNestedContentDefinition)type;
- nestedContentDefinition = nestedSchema.getNestedContentDefinition();
+ nestedContentDefinition = getNestedContentDefinition(nestedSchema);
+
+ // get Choice-Elements to Choice-List (needed for Javascript-ADD-Button)
+ if (nestedContentDefinition.isChoiceType()) {
+
+ Iterator choiceSubElementTypeIterator = nestedContentDefinition.getTypeSequence().iterator();
+ while (choiceSubElementTypeIterator.hasNext()) {
+ // get the type of possible SubElements
+ I_CmsXmlSchemaType choiceSubElementType = (I_CmsXmlSchemaType)choiceSubElementTypeIterator.next();
+
+ // add Choice-Element to Choice-List recursively
+ addChoiceElementToChoiceList(choiceList, nestedContentDefinition, choiceSubElementType);
+ }
+ }
}
// create xpath to the current element
String name = pathPrefix + type.getName();
@@ -1724,10 +1794,49 @@
// get value and corresponding widget
I_CmsXmlContentValue value = elementSequence.getValue(j);
I_CmsWidget widget = null;
- if (type.isSimpleType()) {
- widget = contentDefinition.getContentHandler().getWidget(value);
+
+ // the value will be replaced by the SubElement if it is a ChoiceElement.
+ // so get the Index now bevor it is to late.
+ int valueIndex = value.getIndex();
+
+ // Get ChoiceSubElementsVariables
+ String choiceSubElementPathPrefix = null; // only necessary if the Choice-SubElement is a NestedContent (recursive-Call of this function)
+ CmsXmlContentDefinition choiceSubElementNestedContentDefinition = null; //only used if widget==null
+
+ String label;
+
+ // if this elments has a Value and it is a Choice-Element, than get the chosen ChoiceSubElement
+ if (!disabledElement && !type.isSimpleType() && nestedContentDefinition.isChoiceType()) {
+ ChoiceSubElementValueHolder valueHolder = new ChoiceSubElementValueHolder(nestedContentDefinition, pathPrefix, value, widget);
+
+ // get Lable
+ label = keyDefault(A_CmsWidget.getLabelKey((I_CmsWidgetParameter)value), value.getName());
+ valueHolder.setLabel(label);
+
+ valueHolder = getChoiceSubElement(valueHolder);
+
+ // The new ChoiceSubElementPathPrefix
+ choiceSubElementPathPrefix = valueHolder.getPathPrefix();
+ // The new ChoiceSubElement Value
+ value = valueHolder.getValue();
+ // The Label for this ChoiceSubElement Value (Wihtout Counter. Maybe separate by "/" if it is a recusrsive Choice SubSubElement )
+ label = valueHolder.getLabel();
+ // The new ChoiceSubElement Widget Or NestedContentDefinition (who is certainly no choice nestedContentDefinition.)
+ widget = valueHolder.getWidget();
+ choiceSubElementNestedContentDefinition = valueHolder.getNestedContentDefinition();
+
+ } else {
+ label = keyDefault(A_CmsWidget.getLabelKey((I_CmsWidgetParameter)value), value.getName());
+ if (type.isSimpleType()) {
+ widget = contentDefinition.getContentHandler().getWidget(value);
+ }
}
+ // Befor an empty Lable exists show the element-Name
+ if (CmsStringUtil.isEmpty(label)) {
+ label = value.getName();
+ }
+
// show errors and/or warnings
String key = value.getPath();
if (showErrors
@@ -1786,6 +1895,7 @@
m_warningTabs.add(m_currentTab);
}
}
+
// create label and help bubble cells
result.append("
");
if (!collapseLabel) {
@@ -1795,13 +1905,13 @@
result.append("Disabled");
}
result.append("\">");
- result.append(keyDefault(A_CmsWidget.getLabelKey((I_CmsWidgetParameter)value), value.getName()));
+ result.append(label);
if (elementCount > 1) {
- result.append(" [").append(value.getIndex() + 1).append("]");
+ result.append(" [").append(valueIndex + 1).append("]");
}
result.append(": ");
if (showHelpBubble && (widget != null) && (value.getIndex() == 0)) {
- // show help bubble only on first element of each content definition
+ // show help bubble only on first element of each content definition (if it is a Choice-Value than value.getIndex() is always 0)
result.append(widget.getHelpBubble(getCms(), this, (I_CmsWidgetParameter)value));
} else {
// create empty cell for all following elements
@@ -1812,17 +1922,31 @@
// append individual widget html cell if element is enabled
if (!disabledElement) {
if (widget == null) {
- // recurse into nested type sequence
- String newPath = CmsXmlUtils.createXpathElement(value.getName(), value.getIndex() + 1);
- result.append("
");
+
+ if (nestedContentDefinition.isChoiceType()) {
+ //include subelement
+ String newPath = CmsXmlUtils.createXpathElement(value.getName(), value.getIndex() + 1); // if it is a Choice-Value than value.getIndex() is always 0
+ result.append("
");
+ }
+
} else {
// this is a simple type, display widget
result.append(widget.getDialogWidget(getCms(), this, (I_CmsWidgetParameter)value));
@@ -1835,7 +1959,7 @@
}
// append element operation (add, remove, move) buttons if required
- result.append(buildElementButtons(name, value.getIndex(), addValue, removeValue));
+ result.append(buildElementButtons(name, valueIndex, addValue, removeValue, new JSONArray(choiceList).toString()));
// close row
result.append("
\n");
@@ -1874,6 +1998,211 @@
}
/**
+ * Get some infos of The SubElement of this ChoiceElements:
+ * (a Choice-Element can only have one Subelement.)
+ * - pathPrefix: only necessary if the Choice-SubElement is a NestedContent (for recursive-Call of this function)
+ * - I_CmsXmlContentValue value:
+ * - Widget or NestedContentDefinition)
+ *
+ * return the ValueHolder With the nestedContentDefinition, pathprefix and value of the single SubElement
+ * Or recursively the SubSubElement who is not a Choice NestedContentDefinition.
+ *
+ * @param valueHolder Contains the nestedContentDefinition, pathprefix and value of the parentElement.
+ * @return the ValueHolder With the (nestedContentDefinition or widget), pathprefix and value of the single SubElement (or recursively the SubSubElement who is not a Choice NestedContentDefinition.).
+ * @throws CmsXmlException
+ */
+ private ChoiceSubElementValueHolder getChoiceSubElement(ChoiceSubElementValueHolder valueHolder) throws CmsXmlException {
+
+ String newPath = CmsXmlUtils.createXpathElement(valueHolder.getValue().getName(), valueHolder.getValue().getIndex() + 1);
+
+ // GET new PathPrefix
+ valueHolder.setPathPrefix(valueHolder.getPathPrefix() + newPath + "/");
+
+ // iterate the type sequence and search a value with this type
+ Iterator choiceSubElementTypeIterator = valueHolder.getNestedContentDefinition().getTypeSequence().iterator();
+ while (choiceSubElementTypeIterator.hasNext()) {
+
+ // get the type of possible SubElements
+ I_CmsXmlSchemaType choiceSubElementType = (I_CmsXmlSchemaType)choiceSubElementTypeIterator.next();
+
+
+ // create xpath to the Sub element of this Choice-Element
+ String choiceSubElementName = valueHolder.getPathPrefix() + choiceSubElementType.getName();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("choiceSubElementName: " + choiceSubElementName);
+ }
+
+ // get the element sequence of the current type
+ CmsXmlContentValueSequence choiceSubElementSequence = m_content.getValueSequence(choiceSubElementName, getElementLocale());
+ if (choiceSubElementSequence == null) {
+ // No choiceSubElementType found, goto next choiceSubElementType
+ LOG.error("should not occured");
+ continue;
+ }
+
+ int choiceSubElementCount = choiceSubElementSequence.getElementCount();
+ if (choiceSubElementCount < 1) {
+ // No element found for this choiceSubElementType, goto next choiceSubElementType
+ continue;
+ }
+
+ if (choiceSubElementCount > 1) { // Choice-Sub-Elements can only occurede one time.
+ LOG.warn("choiceSubElementCount > 1: '" + m_content.getFile().getRootPath() + "'; '" + valueHolder.getPathPrefix() + "'");
+ }
+
+ // get the first choiceSubElementValue
+ valueHolder.setValue(choiceSubElementSequence.getValue(0));
+
+ // get Lable
+ String label = keyDefault(A_CmsWidget.getLabelKey((I_CmsWidgetParameter)valueHolder.getValue()), valueHolder.getValue().getName());
+ if (!CmsStringUtil.isEmpty(valueHolder.getLabel())) {
+ if (!CmsStringUtil.isEmpty(label)) {
+ label = valueHolder.getLabel().concat("/").concat(label);
+ }
+ }
+ valueHolder.setLabel(label);
+
+ // get Widget or NestedContentDefinition or recursivly the result of the Choice-NestedContentDefinition
+ if (choiceSubElementType.isSimpleType()) {
+ valueHolder.setWidget(valueHolder.getNestedContentDefinition().getContentHandler().getWidget(valueHolder.getValue()));
+ } else {
+ // get nested content definition for nested types
+ CmsXmlNestedContentDefinition choiceSubElementNestedSchema = (CmsXmlNestedContentDefinition)choiceSubElementType;
+ valueHolder.setNestedContentDefinition(choiceSubElementNestedSchema.getNestedContentDefinition());
+ if (valueHolder.getNestedContentDefinition().isChoiceType()) {
+ LOG.info("Nested ChoiceContentDefinition found: " + valueHolder.getPathPrefix());
+ valueHolder = getChoiceSubElement(valueHolder);
+ }
+ }
+
+ // found one Sub-Element found in Choice-Element
+ return valueHolder; // exit loop
+ }
+
+ LOG.warn("NO nestedContent- Choice-Value found: " + valueHolder.getPathPrefix());
+ return valueHolder;
+ }
+
+ /**
+ * add Choice-Element to Choice-List (needed for Javascript-ADD-Button).
+ *
+ * @param choiceList The ChoiceList where The Choice-Element should be added
+ * @param contentDefinition The contentDefinition who contents this choiceSubElementType
+ * @param choiceSubElementType The element type of the Choice-Element who should be added
+ * @throws JSONException
+ */
+ private void addChoiceElementToChoiceList(List choiceList, CmsXmlContentDefinition contentDefinition, I_CmsXmlSchemaType choiceSubElementType) throws JSONException {
+ addChoiceElementToChoiceList(choiceList, contentDefinition, choiceSubElementType, "", "");
+ }
+
+ /**
+ * add Choice-Element to Choice-List recursively (needed for Javascript-ADD-Button).
+ *
+ * @param choiceList The ChoiceList where The Choice-Element should be added
+ * @param contentDefinition The contentDefinition who contents this choiceSubElementType
+ * @param choiceSubElementType The element type of the Choice-Element who should be added
+ * @param labelPrefix the Labelprefix of the previously called {@link #addChoiceElementToChoiceList(List, CmsXmlContentDefinition, I_CmsXmlSchemaType, String, String)}
+ * @param namePrefix the Nameprefix of the previously called {@link #addChoiceElementToChoiceList(List, CmsXmlContentDefinition, I_CmsXmlSchemaType, String, String)}
+ * @throws JSONException
+ */
+ private void addChoiceElementToChoiceList(List choiceList, CmsXmlContentDefinition contentDefinition, I_CmsXmlSchemaType choiceElementType, String labelPrefix, String namePrefix) throws JSONException {
+ StringBuffer labelKey = new StringBuffer(128);
+ String helpKey;
+ // The following code is not used in XmlContentEditor (Only used in Administration-View etc....)
+// if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(pathPrefix)) {
+// labelKey.append(pathPrefix);
+// labelKey.append('.');
+// }
+ labelKey.append(A_CmsWidget.LABEL_PREFIX);
+ labelKey.append(contentDefinition.getInnerName());
+ labelKey.append('.');
+ labelKey.append(choiceElementType.getName());
+
+ helpKey = labelKey.toString().concat(A_CmsWidget.HELP_POSTFIX);
+
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("labelKey: " + labelKey.toString());
+ LOG.debug("helpKey: " + helpKey);
+ }
+
+ String label = keyDefault(labelKey.toString(), choiceElementType.getName());
+ String help = keyDefault(helpKey, "");
+ String name = choiceElementType.getName();
+
+ // append Prefixes to Name & Label
+ if (!CmsStringUtil.isEmpty(namePrefix)) {
+ name = namePrefix.concat("/").concat(name);
+ }
+ if (!CmsStringUtil.isEmpty(labelPrefix)) {
+ if (!CmsStringUtil.isEmpty(label)) {
+ label = labelPrefix.concat("/").concat(label);
+ }
+ }
+
+
+ // add Choice-Element to Choice-List
+ if (isChoiceType(choiceElementType)) {
+ CmsXmlContentDefinition nestedContentDefinition = getNestedContentDefinition(choiceElementType);
+
+ // recursive call if this is a Choice-NestedContentDefinition
+ Iterator choiceSubElementTypeIterator = nestedContentDefinition.getTypeSequence().iterator();
+ while (choiceSubElementTypeIterator.hasNext()) {
+ // get the type of possible SubElements
+ I_CmsXmlSchemaType choiceSubElementType = (I_CmsXmlSchemaType)choiceSubElementTypeIterator.next();
+
+ // add Choice-Element to Choice-List
+ addChoiceElementToChoiceList(choiceList, nestedContentDefinition, choiceSubElementType, label, name);
+ }
+ } else {
+ JSONObject jsonObj = new JSONObject();
+ jsonObj.put("Label", label);
+ jsonObj.put("Name", name);
+ if (!CmsStringUtil.isEmpty(help)) {
+ jsonObj.put("Help", help);
+ }
+ choiceList.add(jsonObj);
+ }
+ }
+
+ /**
+ * check if this choiceElementType is a Choice-NestedContentDefinition.
+ *
+ * @param choiceElementType true if this element is a choice-element
+ *
+ * @return
+ */
+ private boolean isChoiceType(I_CmsXmlSchemaType choiceElementType) {
+
+ boolean isChoiceType;
+ CmsXmlContentDefinition nestedContentDefinition = null;
+ if (choiceElementType.isSimpleType()) {
+ isChoiceType = false;
+ } else {
+ nestedContentDefinition = getNestedContentDefinition(choiceElementType);
+
+ // get Choice-Elements to Choice-List (needed for Javascript-ADD-Button)
+ if (nestedContentDefinition.isChoiceType()) {
+ isChoiceType = true;
+ } else {
+ isChoiceType = false;
+ }
+ }
+ return isChoiceType;
+ }
+
+ private CmsXmlContentDefinition getNestedContentDefinition(I_CmsXmlSchemaType nestedSchema) {
+
+ if (nestedSchema instanceof CmsXmlNestedContentDefinition) {
+ CmsXmlContentDefinition nestedContentDefinition = ((CmsXmlNestedContentDefinition)nestedSchema).getNestedContentDefinition();
+ return nestedContentDefinition;
+ } else {
+ throw new IllegalArgumentException("The nestedSchema must be an Inxtance of CmsXmlNestedContentDefinition.");
+ }
+ }
+
+ /**
* Resets the error handler member variable to reinitialize the error messages.
*/
private void resetErrorHandler() {
@@ -1908,4 +2237,57 @@
m_file = getCloneCms().writeFile(m_file);
m_content = CmsXmlContentFactory.unmarshal(getCloneCms(), m_file);
}
+
+ /**
+ * The recursive function {@link CmsXmlContentEditor#getChoiceSubElement(ChoiceSubElementValueHolder)}
+ * Returns more then one Value: A ValueHolder is needed.
+ *
+ * @author harald brabenetz
+ */
+ private class ChoiceSubElementValueHolder {
+ private String m_pathPrefix; // only necessary if the Choice-SubElement is a NestedContent (recursive-Calls)
+ private CmsXmlContentDefinition m_nestedContentDefinition; //only used if widget==null
+ private I_CmsXmlContentValue m_value;
+ private I_CmsWidget m_widget;
+ private String m_label;
+
+ public ChoiceSubElementValueHolder(CmsXmlContentDefinition nestedContentDefinition, String pathPrefix, I_CmsXmlContentValue value, I_CmsWidget widget) {
+ super();
+ this.m_nestedContentDefinition = nestedContentDefinition;
+ this.m_pathPrefix = pathPrefix;
+ this.m_value = value;
+ this.m_widget = widget;
+ }
+
+ public CmsXmlContentDefinition getNestedContentDefinition() {
+ return m_nestedContentDefinition;
+ }
+ public void setNestedContentDefinition(CmsXmlContentDefinition nestedContentDefinition) {
+ this.m_nestedContentDefinition = nestedContentDefinition;
+ }
+ public String getPathPrefix() {
+ return m_pathPrefix;
+ }
+ public void setPathPrefix(String pathPrefix) {
+ this.m_pathPrefix = pathPrefix;
+ }
+ public I_CmsXmlContentValue getValue() {
+ return m_value;
+ }
+ public void setValue(I_CmsXmlContentValue value) {
+ this.m_value = value;
+ }
+ public I_CmsWidget getWidget() {
+ return m_widget;
+ }
+ public void setWidget(I_CmsWidget widget) {
+ this.m_widget = widget;
+ }
+ public String getLabel() {
+ return m_label;
+ }
+ public void setLabel(String label) {
+ this.m_label = label;
+ }
+ }
}
\ No newline at end of file
Index: modules/org.opencms.editors/resources/system/workplace/editors/xmlcontent/editor_form.jsp
===================================================================
RCS file: /usr/local/cvs/opencms/modules/org.opencms.editors/resources/system/workplace/editors/xmlcontent/editor_form.jsp,v
retrieving revision 1.15
diff -u -r1.15 editor_form.jsp
--- modules/org.opencms.editors/resources/system/workplace/editors/xmlcontent/editor_form.jsp 9 Jun 2009 12:21:36 -0000 1.15
+++ modules/org.opencms.editors/resources/system/workplace/editors/xmlcontent/editor_form.jsp 20 Jan 2010 19:30:22 -0000
@@ -298,9 +298,42 @@
//-->
+
+