From 52950cfcfbd1f765c4e380fd1cee091d9c2ac633 Mon Sep 17 00:00:00 2001 From: Ariel Backenroth Date: Wed, 2 May 2007 21:56:36 +0000 Subject: [PATCH] xforms ui updates and fixes - WCM-261: only one active tinymce instance per page. also extracted tinymce to what will soon be externally configurable parameters which enables different button configurations based on appearance. having on instance per page does seem to provide a marginal performance boost - WCM-453: better error message when creating content with a filename that already exists. - WCM-431: providing tooltips on label based on a hint annotation - test case for recursive schemas (haven't addressed the bug) - fix for create web content for non form based content git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5603 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/messages/webclient.properties | 1 + .../web/bean/wcm/CreateWebContentWizard.java | 19 +- .../web/forms/xforms/Schema2XForms.java | 60 ++---- .../web/forms/xforms/XFormsProcessor.java | 20 +- .../recursive-test.xsd | 21 ++ .../simple-test/components-test.xsd | 11 +- source/web/css/xforms.css | 16 ++ source/web/scripts/ajax/xforms.js | 188 ++++++++++++------ 8 files changed, 217 insertions(+), 119 deletions(-) create mode 100644 source/test-resources/xforms/unit-tests/interesting-schema-test/recursive-test.xsd 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 } },