diff --git a/config/alfresco/messages/webclient.properties b/config/alfresco/messages/webclient.properties index 2b3540b434..c766f44221 100644 --- a/config/alfresco/messages/webclient.properties +++ b/config/alfresco/messages/webclient.properties @@ -1532,5 +1532,6 @@ validation_provide_values_for_required_fields=Please provide values for all requ idle=Idle loading=Loading eg=e.g. +click_to_edit=click to edit # File Picker go_up=Go up diff --git a/source/java/org/alfresco/web/bean/wcm/CreateWebContentWizard.java b/source/java/org/alfresco/web/bean/wcm/CreateWebContentWizard.java index 7ae2a2ab8e..39f9afe029 100644 --- a/source/java/org/alfresco/web/bean/wcm/CreateWebContentWizard.java +++ b/source/java/org/alfresco/web/bean/wcm/CreateWebContentWizard.java @@ -26,6 +26,7 @@ package org.alfresco.web.bean.wcm; import java.io.ByteArrayInputStream; import java.io.Serializable; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -48,6 +49,7 @@ import org.alfresco.repo.avm.AVMNodeConverter; import org.alfresco.repo.avm.wf.AVMSubmittedAspect; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.workflow.WorkflowModel; +import org.alfresco.service.cmr.avm.AVMExistsException; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.avmsync.AVMDifference; @@ -469,7 +471,7 @@ public class CreateWebContentWizard extends BaseContentWizard final Form form = (MimetypeMap.MIMETYPE_XML.equals(this.mimeType) ? this.getForm() : null); - String path = null; + String path = cwd; if (form != null) { path = form.getOutputPathForFormInstanceData(this.instanceDataDocument, @@ -491,9 +493,18 @@ public class CreateWebContentWizard extends BaseContentWizard LOGGER.debug("creating file " + fileName + " in " + path); // put the content of the file into the AVM store - avmService.createFile(path, - fileName, - new ByteArrayInputStream((this.content == null ? "" : this.content).getBytes("UTF-8"))); + try + { + avmService.createFile(path, + fileName, + new ByteArrayInputStream((this.content == null ? "" : this.content).getBytes("UTF-8"))); + } + catch (AVMExistsException avmee) + { + String msg = Application.getMessage(FacesContext.getCurrentInstance(), "error_exists"); + msg = MessageFormat.format(msg, fileName); + throw new AlfrescoRuntimeException(msg, avmee); + } // remember the created path this.createdPath = AVMNodeConverter.ExtendAVMPath(path, fileName); diff --git a/source/java/org/alfresco/web/forms/xforms/Schema2XForms.java b/source/java/org/alfresco/web/forms/xforms/Schema2XForms.java index 6dc8e707b3..b05ae6955e 100644 --- a/source/java/org/alfresco/web/forms/xforms/Schema2XForms.java +++ b/source/java/org/alfresco/web/forms/xforms/Schema2XForms.java @@ -1868,12 +1868,6 @@ public class Schema2XForms //NamespaceConstants.XFORMS_PREFIX + ":ref", //"."); - Element hint = this.createHint(xformsDocument, owner, resourceBundle); - if (hint != null) - { - formControl.appendChild(hint); - } - //add selector if repeat //if (repeatSection != formSection) //this.addSelector(xformsDocument, (Element) formControl.getParentNode()); @@ -1992,10 +1986,23 @@ public class Schema2XForms alert = ("Please provide a valid value for '" + caption + "'." + " '" + caption + "' is " + (o.minimum == 0 ? "an optional" : "a required") + " '" + - createCaption(this.getXFormsTypeName(xformsDocument, schema, controlType)) + + this.createCaption(this.getXFormsTypeName(xformsDocument, schema, controlType)) + "' value."); } alertElement.appendChild(xformsDocument.createTextNode(alert)); + + final String hint = Schema2XForms.extractPropertyFromAnnotation(NamespaceService.ALFRESCO_URI, + "hint", + this.getAnnotation(owner), + resourceBundle); + if (hint != null) + { + final Element hintElement = xformsDocument.createElementNS(NamespaceConstants.XFORMS_NS, + NamespaceConstants.XFORMS_PREFIX + ":hint"); + formControl.appendChild(hintElement); + this.setXFormsId(hintElement); + hintElement.appendChild(xformsDocument.createTextNode(hint)); + } return formControl; } @@ -2706,45 +2713,6 @@ public class Schema2XForms return control; } - /** - * Creates a hint XML Schema annotated node (AttributeDecl or ElementDecl). - * The implementation is responsible for providing an xforms:hint element for the - * specified schemaNode suitable to be dsipalayed to users of the XForm. The caller - * is responsible for adding the returned element to the form. - * This typically includes extracting documentation from the element/attribute's - * annotation/documentation elements and/or extracting the same information from the - * element/attribute's type annotation/documentation. - * - * @param xformsDocument - * @param node - * @param resourceBundle - * @return The xforms:hint element. If a null value is returned a hint is not added. - */ - public Element createHint(final Document xformsDocument, - final XSObject node, - final ResourceBundle resourceBundle) - { - final XSAnnotation annotation = this.getAnnotation(node); - if (annotation == null) - { - return null; - } - final String s = this.extractPropertyFromAnnotation(NamespaceService.ALFRESCO_URI, - "hint", - annotation, - resourceBundle); - if (s == null) - { - return null; - } - final Element hintElement = - xformsDocument.createElementNS(NamespaceConstants.XFORMS_NS, - NamespaceConstants.XFORMS_PREFIX + ":hint"); - this.setXFormsId(hintElement); - hintElement.appendChild(xformsDocument.createTextNode(s)); - return hintElement; - } - private XSAnnotation getAnnotation(final XSObject o) { return (o instanceof XSElementDeclaration diff --git a/source/java/org/alfresco/web/forms/xforms/XFormsProcessor.java b/source/java/org/alfresco/web/forms/xforms/XFormsProcessor.java index b2f1ae7394..c367334073 100644 --- a/source/java/org/alfresco/web/forms/xforms/XFormsProcessor.java +++ b/source/java/org/alfresco/web/forms/xforms/XFormsProcessor.java @@ -46,6 +46,10 @@ public class XFormsProcessor private static final Log LOGGER = LogFactory.getLog(XFormsProcessor.class); + /** + * A triple of js variable name, namespace uri, and namespace prefix which + * will form javascript variables within alfresco.constants. + */ private final static String[][] JS_NAMESPACES = { { "xforms", NamespaceConstants.XFORMS_NS, NamespaceConstants.XFORMS_PREFIX }, @@ -54,6 +58,7 @@ public class XFormsProcessor { "alfresco", NamespaceService.ALFRESCO_URI, NamespaceService.ALFRESCO_PREFIX } }; + /** Scripts needed to initialize the xforms client. */ private final static String[] JS_SCRIPTS = { "/scripts/tiny_mce/" + (LOGGER.isDebugEnabled() @@ -68,18 +73,19 @@ public class XFormsProcessor "/scripts/upload_helper.js", }; - + /** Localized strings needed by the xforms client. */ private final static String[] BUNDLE_KEYS = { - "validation_provide_values_for_required_fields", + "add_content", + "cancel", + "click_to_edit", + "eg", + "go_up", "idle", "loading", - "add_content", - "go_up", - "cancel", - "upload", "path", - "eg" + "upload", + "validation_provide_values_for_required_fields" }; public XFormsProcessor() diff --git a/source/test-resources/xforms/unit-tests/interesting-schema-test/recursive-test.xsd b/source/test-resources/xforms/unit-tests/interesting-schema-test/recursive-test.xsd new file mode 100644 index 0000000000..ac61994e54 --- /dev/null +++ b/source/test-resources/xforms/unit-tests/interesting-schema-test/recursive-test.xsd @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/source/test-resources/xforms/unit-tests/simple-test/components-test.xsd b/source/test-resources/xforms/unit-tests/simple-test/components-test.xsd index 3d2af4dd27..b0e598e8ea 100644 --- a/source/test-resources/xforms/unit-tests/simple-test/components-test.xsd +++ b/source/test-resources/xforms/unit-tests/simple-test/components-test.xsd @@ -154,13 +154,19 @@ - full + + full + this control should appear as radio buttons. + - full + + full + Please select some of these numbers. This control should appear as a list. + @@ -169,6 +175,7 @@ ${components-test.list_of_ten_select_three.label} ${components-test.list_of_ten_select_three.alert} + Please select exactly three elements from this list. diff --git a/source/web/css/xforms.css b/source/web/css/xforms.css index 5d20237995..65287c417b 100644 --- a/source/web/css/xforms.css +++ b/source/web/css/xforms.css @@ -97,6 +97,22 @@ height: 200px; } +.xformsRichTextEditorHoverLayer +{ + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + background-color: lightgray; + color: black; + z-index: 100; + font-weight: bolder; + font-size: 16px; + text-align: center; + opacity: .5; +} + .xformsRepeat { } diff --git a/source/web/scripts/ajax/xforms.js b/source/web/scripts/ajax/xforms.js index fe1872c62f..23691a6c87 100644 --- a/source/web/scripts/ajax/xforms.js +++ b/source/web/scripts/ajax/xforms.js @@ -386,18 +386,6 @@ dojo.declare("alfresco.xforms.Widget", return xpath; }, - /** Returns the label node for this widget from the xforms document. */ - _getLabelNode: function() - { - return this._getChildXFormsNode("label"); - }, - - /** Returns the alert node for this widget from the xforms document. */ - _getAlertNode: function() - { - return this._getChildXFormsNode("alert"); - }, - /** Returns a child node by name within the xform. */ _getChildXFormsNode: function(nodeName) { @@ -418,7 +406,7 @@ dojo.declare("alfresco.xforms.Widget", /** Returns the widget's label. */ getLabel: function() { - var node = this._getLabelNode(); + var node = this._getChildXFormsNode("label"); var result = node ? dojo.dom.textContent(node) : ""; if (djConfig.isDebug) { @@ -430,9 +418,16 @@ dojo.declare("alfresco.xforms.Widget", /** Returns the widget's alert text. */ getAlert: function() { - var node = this._getAlertNode(); + var node = this._getChildXFormsNode("alert"); return node ? dojo.dom.textContent(node) : ""; }, + + /** Returns the widget's alert text. */ + getHint: function() + { + var node = this._getChildXFormsNode("hint"); + return node ? dojo.dom.textContent(node) : null; + }, /** Makes the label red. */ showAlert: function() @@ -816,24 +811,44 @@ dojo.declare("alfresco.xforms.PlainTextEditor", /** The textfield widget which handle xforms widget xf:textarea. with appearance full or compact */ dojo.declare("alfresco.xforms.RichTextEditor", alfresco.xforms.Widget, - function(xform, xformsNode) + function(xform, xformsNode, params) { - this.focused = false; + this._focused = false; + this._tinyMCE_buttons = params; + if (!this.statics.tinyMCEInitialized) + { + this.statics.tinyMCEInitialized = true; + } }, { ///////////////////////////////////////////////////////////////// - // methods + // methods & properties ///////////////////////////////////////////////////////////////// + + statics: { currentInstance: null, tinyMCEInitialized: false }, _removeTinyMCE: function() { var value = tinyMCE.getContent(this.id); + this._commitValueChange(); tinyMCE.removeMCEControl(this.id); + this._focused = false; }, _createTinyMCE:function() { + if (this.statics.currentInstance && + this.statics.currentInstance != this) + { + this.statics.currentInstance._removeTinyMCE(); + } + + this.statics.currentInstance = this; + + tinyMCE.settings.theme_advanced_buttons1 = this._tinyMCE_buttons[0]; + tinyMCE.settings.theme_advanced_buttons2 = this._tinyMCE_buttons[1]; + tinyMCE.settings.theme_advanced_buttons3 = this._tinyMCE_buttons[2]; tinyMCE.addMCEControl(this.widget, this.id); var editorDocument = tinyMCE.getInstanceById(this.id).getDoc(); @@ -856,6 +871,8 @@ dojo.declare("alfresco.xforms.RichTextEditor", this.widget = document.createElement("div"); this.domNode.appendChild(this.widget); dojo.html.prependClass(this.widget, "xformsTextArea"); + this.widget.style.border = "1px solid black"; + this.widget.style.overflow = "auto"; this.widget.innerHTML = this.getInitialValue() || ""; var images = this.widget.getElementsByTagName("img"); for (var i = 0; i < images.length; i++) @@ -868,17 +885,18 @@ dojo.declare("alfresco.xforms.RichTextEditor", } if (!this.isReadonly()) { - this._createTinyMCE(); +// this._createTinyMCE(); + var me = this; + dojo.event.browser.addListener(this.widget, + "onmouseover", + function(event) { me._div_mouseoverHandler(event) }, + true); } }, setValue: function(value) { - if (this.isReadonly()) - { - this.widget.innerHTML = value; - } - else + if (this.statics.currentInstance == this) { tinyMCE.selectedInstance = tinyMCE.getInstanceById(this.id); try @@ -891,11 +909,15 @@ dojo.declare("alfresco.xforms.RichTextEditor", dojo.debug(e); } } + else + { + this.widget.innerHTML = value; + } }, getValue: function() { - var result = this.isReadonly() ? this.widget.innerHTML : tinyMCE.getContent(this.id); + var result = this.statics.currentInstance == this ? tinyMCE.getContent(this.id) : this.widget.innerHTML; result = result.replace(new RegExp(alfresco.constants.AVM_WEBAPP_URL, "g"), ""); return result; }, @@ -903,15 +925,10 @@ dojo.declare("alfresco.xforms.RichTextEditor", setReadonly: function(readonly) { alfresco.xforms.RichTextEditor.superclass.setReadonly.call(this, readonly); - var mce = tinyMCE.getInstanceById(this.id); - if (readonly && mce) + if (readonly && this.statics.currentInstance == this) { this._removeTinyMCE(); } - else if (!readonly && !mce && this.widget) - { - this._createTinyMCE(); - } }, _destroy: function() @@ -936,14 +953,14 @@ dojo.declare("alfresco.xforms.RichTextEditor", } var widget = event.target.widget; widget._commitValueChange(); - this.focused = false; + this._focused = false; }, _tinyMCE_focusHandler: function(event) { var widget = event.target.widget; var repeatIndices = widget.getRepeatIndices(); - if (repeatIndices.length != 0 && !this.focused) + if (repeatIndices.length != 0 && !this._focused) { var r = repeatIndices[repeatIndices.length - 1].repeat; var p = widget; @@ -962,7 +979,49 @@ dojo.declare("alfresco.xforms.RichTextEditor", } repeatIndices[repeatIndices.length - 1].repeat.setFocusedChild(p); } - this.focused = true; + this._focused = true; + }, + + _div_mouseoverHandler: function(event) + { + if (!this.hoverLayer) + { + this.hoverLayer = document.createElement("div"); + dojo.html.setClass(this.hoverLayer, "xformsRichTextEditorHoverLayer"); + this.hoverLayer.appendChild(document.createTextNode(alfresco.xforms.constants.resources["click_to_edit"])); + } + if (!this.hoverLayer.parentNode) + { + this.widget.appendChild(this.hoverLayer); + this.hoverLayer.style.lineHeight = this.hoverLayer.offsetHeight + "px"; + var me = this; + dojo.event.browser.addListener(this.hoverLayer, + "onmouseout", + function(event) { me._hoverLayer_mouseoutHandler(event) }, + true); + + dojo.event.browser.addListener(this.hoverLayer, + "onclick", + function(event) { me._hoverLayer_clickHandler(event); }, + true); + } + }, + + _hoverLayer_mouseoutHandler: function(event) + { + if (this.hoverLayer.parentNode) + { + this.widget.removeChild(this.hoverLayer); + } + }, + + _hoverLayer_clickHandler: function(event) + { + if (this.hoverLayer.parentNode) + { + this.widget.removeChild(this.hoverLayer); + this._createTinyMCE(); + } } }); @@ -2325,7 +2384,15 @@ dojo.declare("alfresco.xforms.AbstractGroup", child.labelNode.style.marginRight = "5px"; labelNode.appendChild(child.labelNode); child.labelNode.appendChild(document.createTextNode(label)); + } + var hint = child.getHint(); + if (hint) + { + labelNode.setAttribute("title", hint); + requiredImage.setAttribute("alt", hint); + } + return labelNode; }, @@ -3943,7 +4010,7 @@ dojo.declare("alfresco.xforms.XForm", { return null; } - var result = new x(this, xformsNode); + var result = new x.className(this, xformsNode, x.params); if (result instanceof alfresco.xforms.Widget) { return result; @@ -4568,7 +4635,6 @@ dojo.html.toCamelCase = function(str) //////////////////////////////////////////////////////////////////////////////// // tiny mce integration //////////////////////////////////////////////////////////////////////////////// - tinyMCE.init({ theme: "advanced", mode: "exact", @@ -4582,8 +4648,8 @@ tinyMCE.init({ execcommand_callback: "alfresco_TinyMCE_execcommand_callback", theme_advanced_toolbar_location: "top", theme_advanced_toolbar_align: "left", - theme_advanced_buttons1: "bold,italic,underline,strikethrough,separator,fontselect,fontsizeselect", - theme_advanced_buttons2: "link,unlink,image,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,bullist,numlist,separator,undo,redo,separator,forecolor,backcolor", + theme_advanced_buttons1: "", + theme_advanced_buttons2: "", theme_advanced_buttons3: "", urlconverter_callback: "alfresco_TinyMCE_urlconverter_callback" }); @@ -4791,62 +4857,64 @@ alfresco.xforms.widgetConfig = { "xf:group": { - "*": { "minimal": alfresco.xforms.HGroup, "*": alfresco.xforms.VGroup } + "*": { "minimal": { "className": alfresco.xforms.HGroup }, "*": { "className": alfresco.xforms.VGroup }} }, "xf:repeat": { - "*": { "*": alfresco.xforms.Repeat } + "*": { "*": { "className": alfresco.xforms.Repeat } } }, "xf:textarea": { - "*": { "minimal": alfresco.xforms.PlainTextEditor, "*": alfresco.xforms.RichTextEditor }, + "*": { "minimal": { "className": alfresco.xforms.PlainTextEditor }, + "*": { "className": alfresco.xforms.RichTextEditor, params: [ "bold,italic,underline,separator,forecolor,backcolor,separator,link,unlink,image", "", "" ] }, + "full": { "className": alfresco.xforms.RichTextEditor, params: ["bold,italic,underline,strikethrough,separator,fontselect,fontsizeselect", "link,unlink,image,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,bullist,numlist,separator,undo,redo,separator,forecolor,backcolor", "" ] }}, }, "xf:upload": { - "*": { "*": alfresco.xforms.FilePicker } + "*": { "*": { "className": alfresco.xforms.FilePicker } } }, "xf:range": { - "*": { "*": alfresco.xforms.NumericalRange } + "*": { "*": { "className": alfresco.xforms.NumericalRange } } }, "xf:input": { - "date": { "*": alfresco.xforms.DatePicker }, - "time": { "*": alfresco.xforms.TimePicker }, - "gDay": { "*": alfresco.xforms.DayPicker }, - "gMonth": { "*": alfresco.xforms.MonthPicker }, - "gYear": { "*": alfresco.xforms.YearPicker }, - "gMonthDay": { "*": alfresco.xforms.MonthDayPicker }, - "gYearMonth": { "*": alfresco.xforms.YearMonthPicker }, - "dateTime": { "*": alfresco.xforms.DateTimePicker }, - "*": { "*": alfresco.xforms.TextField } + "date": { "*": { "className": alfresco.xforms.DatePicker }}, + "time": { "*": { "className": alfresco.xforms.TimePicker }}, + "gDay": { "*": { "className": alfresco.xforms.DayPicker }}, + "gMonth": { "*": { "className": alfresco.xforms.MonthPicker }}, + "gYear": { "*": { "className": alfresco.xforms.YearPicker }}, + "gMonthDay": { "*": { "className": alfresco.xforms.MonthDayPicker }}, + "gYearMonth": { "*": { "className": alfresco.xforms.YearMonthPicker }}, + "dateTime": { "*": { "className": alfresco.xforms.DateTimePicker }}, + "*": { "*": { "className": alfresco.xforms.TextField }} }, "xf:select1": { - "boolean": { "*": alfresco.xforms.Checkbox }, - "*": { "full": alfresco.xforms.RadioSelect1, - "*": alfresco.xforms.ComboboxSelect1 } + "boolean": { "*": { "className": alfresco.xforms.Checkbox }}, + "*": { "full": { "className": alfresco.xforms.RadioSelect1}, + "*": { "className": alfresco.xforms.ComboboxSelect1 }} }, "xf:select": { - "*": { "full": alfresco.xforms.CheckboxSelect, - "*": alfresco.xforms.ListSelect } + "*": { "full": { "className": alfresco.xforms.CheckboxSelect}, + "*": { "className": alfresco.xforms.ListSelect }} }, "xf:submit": { - "*": { "*": alfresco.xforms.Submit } + "*": { "*": { "className": alfresco.xforms.Submit } } }, "xf:trigger": { - "*": { "*": alfresco.xforms.Trigger } + "*": { "*": { "className": alfresco.xforms.Trigger }} }, "xf:switch": { - "*": { "*": alfresco.xforms.SwitchGroup } + "*": { "*": { "className": alfresco.xforms.SwitchGroup } } }, "xf:case": { - "*": { "*": alfresco.xforms.CaseGroup } + "*": { "*": { "className": alfresco.xforms.CaseGroup }} }, "chiba:data": { "*": { "*": null } }, "xf:label": { "*": { "*": null } },