I'm implementing a bit of XSLT which will add a notification to be
displayed to the user when they try and submit a form with invalid data/missing required fields. Following the example of the DMV detail form, I've got an output which displays on a xforms-submit-error event - this works well enough. But the problem is this event can represent two very different things: 1) the user omitting to fill a required field/entering invalid data 2) the backend server rejecting the submission, or being down, or a network connection failing etc etc. If you start displaying messages like '...check for invalid values' for (2), you'll just confuse & frustrate the user as they check and recheck their form for bad data and keep trying to submit, as well as masking potential bugs & environmental problems - bad. To try and distinguish between the two cases, at first I thought I could use a top level xforms-invalid handler which sets a flag indicating that the problem is definitely with invalid data. However, the big problem with this is that xforms-invalid *doesn't* get fired when you leave a required field blank (it's not considered invalid). So this approach is no good, since missing required fields are the most common scenario for a bad submission. Has anyone come up with any other approach to distinguishing between xforms-submit-error causes? I really really don't want to have some sort of catch-all message that has to say "Your form couldn't be submitted... it's *probably* because you left a field blank, or added some bad data, but it MIGHT be because something screwed up on the back end, in which case there's nothing you can do." Hardly reassuring to the user! Frankly the more I try and use required fields, the more broken their specification seems. The decision in the XForms 1.0 errata to clarify missing required fields as *not* being considered invalid seems to have left some rather gaping holes: - the lack of a xforms-required-but-empty pseudoclass - xforms-required-empty & xforms-required-filled events (to match xforms-valid & xforms-invalid events) - any indication when the unspecified required-but-empty state should be updated to the UI It's disappointing that there's no sign that any aspect of required fields is being addressed in the XForms 1.1 draft. If I can solve the problem of not displaying alerts on startup, the best approach might be to avoid required-ness altogether and use a constraint instead. Adrian -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help ObjectWeb mailing lists service home page: http://www.objectweb.org/wws |
Adrian Baker wrote:
> I'm implementing a bit of XSLT which will add a notification to be > displayed to the user when they try and submit a form with invalid > data/missing required fields. > > Following the example of the DMV detail form, I've got an output which > displays on a xforms-submit-error event - this works well enough. > > But the problem is this event can represent two very different things: > 1) the user omitting to fill a required field/entering invalid data > 2) the backend server rejecting the submission, or being down, or a > network connection failing etc etc. > > If you start displaying messages like '...check for invalid values' > for (2), you'll just confuse & frustrate the user as they check and > recheck their form for bad data and keep trying to submit, as well as > masking potential bugs & environmental problems - bad. > > To try and distinguish between the two cases, at first I thought I > could use a top level xforms-invalid handler which sets a flag > indicating that the problem is definitely with invalid data. However, > the big problem with this is that xforms-invalid *doesn't* get fired > when you leave a required field blank (it's not considered invalid). > So this approach is no good, since missing required fields are the > most common scenario for a bad submission. > > Has anyone come up with any other approach to distinguishing between > xforms-submit-error causes? I really really don't want to have some > sort of catch-all message that has to say "Your form couldn't be > submitted... it's *probably* because you left a field blank, or added > some bad data, but it MIGHT be because something screwed up on the > back end, in which case there's nothing you can do." Hardly reassuring > to the user! > > Frankly the more I try and use required fields, the more broken their > specification seems. The decision in the XForms 1.0 errata to clarify > missing required fields as *not* being considered invalid seems to > have left some rather gaping holes: > - the lack of a xforms-required-but-empty pseudoclass > - xforms-required-empty & xforms-required-filled events (to match > xforms-valid & xforms-invalid events) > - any indication when the unspecified required-but-empty state should > be updated to the UI > > It's disappointing that there's no sign that any aspect of required > fields is being addressed in the XForms 1.1 draft. If I can solve the > problem of not displaying alerts on startup, the best approach might > be to avoid required-ness altogether and use a constraint instead. > > Adrian > xforms-submit-serialize (http://www.w3.org/TR/xforms11/#evt-submit-serialize) to set a flag which would indicate validation had passed and that an actual submission is about to be attempted. This event looks like it would be pretty straightforward to fire from XFormsModelSubmission at the appropriate point - would a patch to this class adding the 1.1 event be accepted? Adrian -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help ObjectWeb mailing lists service home page: http://www.objectweb.org/wws |
Administrator
|
Hi Adrian,
I like your solution of using xforms-submit-serialize. Certainly, if you get a chance to implement this, we'll look into integrating your source in the codebase. Once this is done, we should also build an example that shows how this event can be used in conjunction with xforms-submit-error to display a message on submission if some fields are invalid. Alex On 6/13/06, Adrian Baker <[hidden email]> wrote: > Adrian Baker wrote: > > I'm implementing a bit of XSLT which will add a notification to be > > displayed to the user when they try and submit a form with invalid > > data/missing required fields. > > > > Following the example of the DMV detail form, I've got an output which > > displays on a xforms-submit-error event - this works well enough. > > > > But the problem is this event can represent two very different things: > > 1) the user omitting to fill a required field/entering invalid data > > 2) the backend server rejecting the submission, or being down, or a > > network connection failing etc etc. > > > > If you start displaying messages like '...check for invalid values' > > for (2), you'll just confuse & frustrate the user as they check and > > recheck their form for bad data and keep trying to submit, as well as > > masking potential bugs & environmental problems - bad. > > > > To try and distinguish between the two cases, at first I thought I > > could use a top level xforms-invalid handler which sets a flag > > indicating that the problem is definitely with invalid data. However, > > the big problem with this is that xforms-invalid *doesn't* get fired > > when you leave a required field blank (it's not considered invalid). > > So this approach is no good, since missing required fields are the > > most common scenario for a bad submission. > > > > Has anyone come up with any other approach to distinguishing between > > xforms-submit-error causes? I really really don't want to have some > > sort of catch-all message that has to say "Your form couldn't be > > submitted... it's *probably* because you left a field blank, or added > > some bad data, but it MIGHT be because something screwed up on the > > back end, in which case there's nothing you can do." Hardly reassuring > > to the user! > > > > Frankly the more I try and use required fields, the more broken their > > specification seems. The decision in the XForms 1.0 errata to clarify > > missing required fields as *not* being considered invalid seems to > > have left some rather gaping holes: > > - the lack of a xforms-required-but-empty pseudoclass > > - xforms-required-empty & xforms-required-filled events (to match > > xforms-valid & xforms-invalid events) > > - any indication when the unspecified required-but-empty state should > > be updated to the UI > > > > It's disappointing that there's no sign that any aspect of required > > fields is being addressed in the XForms 1.1 draft. If I can solve the > > problem of not displaying alerts on startup, the best approach might > > be to avoid required-ness altogether and use a constraint instead. > > > > Adrian > > > > One approach that has occurred to me is to use the new XForms 1.1 event > xforms-submit-serialize > (http://www.w3.org/TR/xforms11/#evt-submit-serialize) to set a flag > which would indicate validation had passed and that an actual submission > is about to be attempted. > > This event looks like it would be pretty straightforward to fire from > XFormsModelSubmission at the appropriate point - would a patch to this > class adding the 1.1 event be accepted? > > Adrian > > > > > > -- > You receive this message as a subscriber of the [hidden email] mailing list. > To unsubscribe: mailto:[hidden email] > For general help: mailto:[hidden email]?subject=help > ObjectWeb mailing lists service home page: http://www.objectweb.org/wws > > > -- Blog (XML, Web apps, Open Source): http://www.orbeon.com/blog/ -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help ObjectWeb mailing lists service home page: http://www.objectweb.org/wws
--
Follow Orbeon on Twitter: @orbeon Follow me on Twitter: @avernet |
Attached is the patch for xforms-submit-serialize. The only uncertainty
I had was whether I needed to include the context information described
in the spec in XFormsSubmitSerializeEvent (currently it records nothing
but the target).
It works well as a solution: I just use the event to set a @validated flag in an instance document which is used to decide between a "there was a backend error" or "your data is invalid" type of message: <xforms:instance id="xformsSubmitError"> <xformsSubmitError validated="false">false</xformsSubmitError> </xforms:instance> <xforms:submission .... > <!-- Reset validated flag on a new submission. --> <xforms:setvalue ev:event="xforms-submit" ref="instance('xformsSubmitError')/@validated" value="false()"/> <!-- We're about to serialize, so the form must have validated successfully. --> <xforms:setvalue ev:event="xforms-submit-serialize" ref="instance('xformsSubmitError')/@validated" value="true()"/> <!-- There was an error submitting. --> <xforms:setvalue ev:event="xforms-submit-error" ref="instance('xformsSubmitError')" value="true()"/> <!-- Submission was successful. --> <xforms:setvalue ev:event="xforms-submit" ref="instance('xformsSubmitError')" value="false()"/> </xforms:submission> Of course, it would be easier if there were two types of xforms-submit-error events! Adrian Alessandro Vernet wrote: Hi Adrian, /** * Copyright (C) 2005 Orbeon, Inc. * * This program 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 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 Lesser General Public License for more details. * * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html */ package org.orbeon.oxf.xforms.event; /** * XForms events definitions. */ public class XFormsEvents { // Custom initialization events public static final String XXFORMS_INITIALIZE = "xxforms-initialize"; public static final String XXFORMS_INITIALIZE_CONTROLS = "xxforms-initialize-controls"; public static final String XXFORMS_INITIALIZE_STATE = "xxforms-initialize-state"; public static final String XXFORMS_ALL_EVENTS_REQUIRED = "xxforms-all-events-required"; // Custom submit event public static final String XXFORMS_SUBMIT = "xxforms-submit"; // Custom load event public static final String XXFORMS_LOAD = "xxforms-load"; // Standard sequences public static final String XXFORMS_VALUE_CHANGE_WITH_FOCUS_CHANGE = "xxforms-value-change-with-focus-change"; // Standard XForms events public static final String XFORMS_MODEL_CONSTRUCT = "xforms-model-construct"; public static final String XFORMS_MODEL_CONSTRUCT_DONE = "xforms-model-construct-done"; public static final String XFORMS_READY = "xforms-ready"; public static final String XFORMS_MODEL_DESTRUCT = "xforms-model-destruct"; public static final String XFORMS_REBUILD = "xforms-rebuild"; public static final String XFORMS_RECALCULATE = "xforms-recalculate"; public static final String XFORMS_REVALIDATE = "xforms-revalidate"; public static final String XFORMS_REFRESH = "xforms-refresh"; public static final String XFORMS_RESET = "xforms-reset"; public static final String XFORMS_SUBMIT = "xforms-submit"; public static final String XFORMS_SUBMIT_SERIALIZE = "xforms-submit-serialize"; public static final String XFORMS_SUBMIT_DONE = "xforms-submit-done"; public static final String XFORMS_VALUE_CHANGED = "xforms-value-changed"; public static final String XFORMS_VALID = "xforms-valid"; public static final String XFORMS_INVALID = "xforms-invalid"; public static final String XFORMS_REQUIRED = "xforms-required"; public static final String XFORMS_OPTIONAL = "xforms-optional"; public static final String XFORMS_READWRITE = "xforms-readwrite"; public static final String XFORMS_READONLY = "xforms-readonly"; public static final String XFORMS_ENABLED = "xforms-enabled"; public static final String XFORMS_DISABLED = "xforms-disabled"; public static final String XFORMS_DESELECT = "xforms-deselect"; public static final String XFORMS_SELECT = "xforms-select"; public static final String XFORMS_INSERT = "xforms-insert"; public static final String XFORMS_DELETE = "xforms-delete"; public static final String XFORMS_FOCUS = "xforms-focus"; public static final String XFORMS_SCROLL_FIRST = "xforms-scroll-first"; public static final String XFORMS_SCROLL_LAST = "xforms-scroll-last"; // DOM events public static final String XFORMS_DOM_ACTIVATE = "DOMActivate"; public static final String XFORMS_DOM_FOCUS_OUT = "DOMFocusOut"; public static final String XFORMS_DOM_FOCUS_IN = "DOMFocusIn"; // Exceptions and errors public static final String XFORMS_LINK_EXCEPTION = "xforms-link-exception"; public static final String XFORMS_LINK_ERROR = "xforms-link-error"; public static final String XFORMS_COMPUTE_EXCEPTION = "xforms-compute-exception"; public static final String XFORMS_SUBMIT_ERROR = "xforms-submit-error"; public static final String XFORMS_BINDING_EXCEPTION = "xforms-binding-exception"; private XFormsEvents() {} } /** * Copyright (C) 2005 Orbeon, Inc. * * This program 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 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 Lesser General Public License for more details. * * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html */ package org.orbeon.oxf.xforms.event; import org.dom4j.Element; import org.orbeon.oxf.common.OXFException; import org.orbeon.oxf.xforms.event.events.*; /** * Factory for XForms events */ public class XFormsEventFactory { public static XFormsEvent createEvent(String newEventName, XFormsEventTarget targetObject) { return createEvent(newEventName, targetObject, null, false, true, true, null, null, null, null); } public static XFormsEvent createEvent(String newEventName, XFormsEventTarget targetObject, XFormsEventTarget otherTargetObject, String contextString, Element contextElement, Throwable contextThrowable, Element filesElement) { return createEvent(newEventName, targetObject, otherTargetObject, false, true, true, contextString, contextElement, contextThrowable, filesElement); } public static XFormsEvent createEvent(String newEventName, XFormsEventTarget targetObject, boolean bubbles, boolean cancelable) { return createEvent(newEventName, targetObject, null, true, bubbles, cancelable, null, null, null, null); } private static XFormsEvent createEvent(String eventName, XFormsEventTarget targetObject, XFormsEventTarget otherTargetObject, boolean allowCustomEvents, boolean bubbles, boolean cancelable, String contextString, Element contextElement, Throwable contextThrowable, Element filesElement) { // TODO: more efficient way to switch! if (eventName.equals(XFormsEvents.XFORMS_DOM_ACTIVATE)) { return new XFormsDOMActivateEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_COMPUTE_EXCEPTION)) { return new org.orbeon.oxf.xforms.event.events.XFormsComputeExceptionEvent(targetObject, contextString, contextThrowable); } else if (eventName.equals(XFormsEvents.XFORMS_DELETE)) { return new XFormsDeleteEvent(targetObject, contextString); } else if (eventName.equals(XFormsEvents.XFORMS_DESELECT)) { return new XFormsDeselectEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_INSERT)) { return new XFormsInsertEvent(targetObject, contextString); } else if (eventName.equals(XFormsEvents.XFORMS_LINK_ERROR)) { return new XFormsLinkErrorEvent(targetObject, contextString, contextElement, contextThrowable); } else if (eventName.equals(XFormsEvents.XFORMS_LINK_EXCEPTION)) { return new XFormsLinkExceptionEvent(targetObject, contextString, contextElement, contextThrowable); } else if (eventName.equals(XFormsEvents.XFORMS_BINDING_EXCEPTION)) { return new XFormsBindingExceptionEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_REFRESH)) { return new XFormsRefreshEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_SELECT)) { return new XFormsSelectEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_SUBMIT_ERROR)) { return new XFormsSubmitErrorEvent(targetObject, contextString, contextThrowable); } else if (eventName.equals(XFormsEvents.XFORMS_SUBMIT)) { return new XFormsSubmitEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_SUBMIT_SERIALIZE)) { return new XFormsSubmitSerializeEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_SUBMIT_DONE)) { return new XFormsSubmitDoneEvent(targetObject); } else if (eventName.equals(XFormsEvents.XXFORMS_VALUE_CHANGE_WITH_FOCUS_CHANGE)) { return new XXFormsValueChangeWithFocusChangeEvent(targetObject, otherTargetObject, contextString); } else if (eventName.equals(XFormsEvents.XXFORMS_SUBMIT)) { return new XXFormsSubmissionEvent(targetObject, filesElement); } else if (eventName.equals(XFormsEvents.XXFORMS_LOAD)) { return new XXFormsLoadEvent(targetObject, contextString); } else if (eventName.equals(XFormsEvents.XFORMS_MODEL_CONSTRUCT)) { return new XFormsModelConstructEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_MODEL_DESTRUCT)) { return new XFormsModelDestructEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_RESET)) { return new XFormsResetEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_MODEL_CONSTRUCT_DONE)) { return new XFormsModelConstructDoneEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_READY)) { return new XFormsReadyEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_REBUILD)) { return new XFormsRebuildEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_RECALCULATE)) { return new XFormsRecalculateEvent(targetObject, Boolean.valueOf(contextString).booleanValue()); } else if (eventName.equals(XFormsEvents.XFORMS_REVALIDATE)) { return new XFormsRevalidateEvent(targetObject, Boolean.valueOf(contextString).booleanValue()); } else if (eventName.equals(XFormsEvents.XFORMS_VALUE_CHANGED)) { return new XFormsValueChangeEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_DOM_FOCUS_OUT)) { return new XFormsDOMFocusOutEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_DOM_FOCUS_IN)) { return new XFormsDOMFocusInEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_VALID)) { return new XFormsValidEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_INVALID)) { return new XFormsInvalidEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_REQUIRED)) { return new XFormsRequiredEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_OPTIONAL)) { return new XFormsOptionalEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_READWRITE)) { return new XFormsReadwriteEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_READONLY)) { return new XFormsReadonlyEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_ENABLED)) { return new XFormsEnabledEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_DISABLED)) { return new XFormsDisabledEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_FOCUS)) { return new XFormsFocusEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_SCROLL_FIRST)) { return new XFormsScrollFirstEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_SCROLL_LAST)) { return new XFormsScrollLastEvent(targetObject); } else if (eventName.equals(XFormsEvents.XFORMS_LINK_EXCEPTION)) { return new XFormsLinkExceptionEvent(targetObject, contextString, contextElement, contextThrowable); } else if (eventName.equals(XFormsEvents.XFORMS_LINK_ERROR)) { return new XFormsLinkErrorEvent(targetObject, contextString, contextElement, contextThrowable); } else if (allowCustomEvents) { return new XFormsCustomEvent(eventName, targetObject, bubbles, cancelable); } else { throw new OXFException("Event factory could not find event with name: " + eventName); } } private XFormsEventFactory() {} } /* * Copyright (c) Orchestral Developments Ltd (2001 - 2004). * * This document is copyright. Except for the purpose of fair reviewing, no part * of this publication may be reproduced or transmitted in any form or by any * means, electronic or mechanical, including photocopying, recording, or any * information storage and retrieval system, without permission in writing from * the publisher. Infringers of copyright render themselves liable for * prosecution. * * $Id$ */ package org.orbeon.oxf.xforms.event.events; import org.orbeon.oxf.xforms.event.XFormsEvent; import org.orbeon.oxf.xforms.event.XFormsEventTarget; import org.orbeon.oxf.xforms.event.XFormsEvents; /** * 4.1.1 The xforms-submit-serialize Event * * Target: submission / Bubbles: Yes / Cancelable: No / Context Info: A node * into which data to be submitted can be placed. * * The default action for this event is to perform the normal XForms submission * serialization if the event context node's content is empty. The content of * the event context node is the data sent by the XForms submission. */ public class XFormsSubmitSerializeEvent extends XFormsEvent { public XFormsSubmitSerializeEvent(final XFormsEventTarget targetObject) { super(XFormsEvents.XFORMS_SUBMIT_SERIALIZE, targetObject, true, false); } } /** * Copyright (C) 2005 Orbeon, Inc. * * This program 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 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 Lesser General Public License for more details. * * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html */ package org.orbeon.oxf.xforms; import org.apache.log4j.Logger; import org.dom4j.*; import org.dom4j.io.DocumentSource; import org.orbeon.oxf.common.OXFException; import org.orbeon.oxf.pipeline.api.ExternalContext; import org.orbeon.oxf.pipeline.api.PipelineContext; import org.orbeon.oxf.processor.ProcessorUtils; import org.orbeon.oxf.util.LoggerFactory; import org.orbeon.oxf.util.NetUtils; import org.orbeon.oxf.xforms.action.XFormsActionInterpreter; import org.orbeon.oxf.xforms.controls.UploadControlInfo; import org.orbeon.oxf.xforms.event.*; import org.orbeon.oxf.xforms.event.events.*; import org.orbeon.oxf.xforms.mip.BooleanModelItemProperty; import org.orbeon.oxf.xforms.mip.ValidModelItemProperty; import org.orbeon.oxf.xml.TransformerUtils; import org.orbeon.oxf.xml.XMLConstants; import org.orbeon.oxf.xml.XMLUtils; import org.orbeon.oxf.xml.dom4j.Dom4jUtils; import org.orbeon.oxf.xml.dom4j.LocationData; import org.orbeon.oxf.xml.dom4j.LocationDocumentResult; import javax.xml.transform.Transformer; import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import java.io.*; import java.net.URI; import java.net.URLEncoder; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Represents an XForms model submission instance. * * TODO: This badly needs to be modularized instead of being a soup of "ifs"! */ public class XFormsModelSubmission implements XFormsEventTarget, XFormsEventHandlerContainer { public final static Logger logger = LoggerFactory.createLogger(XFormsModelSubmission.class); private final XFormsContainingDocument containingDocument; private final String id; private final XFormsModel model; private final Element submissionElement; private boolean submissionElementExtracted = false; // Event handlers private final List eventHandlers; private String avtAction; // required private String resolvedAction; private String method; // required private boolean validate = true; private boolean relevant = true; private String version; private boolean indent; private String mediatype; private String encoding; private boolean omitxmldeclaration; private Boolean standalone; private String cdatasectionelements; private String replace = XFormsConstants.XFORMS_SUBMIT_REPLACE_ALL; private String replaceInstanceId; private String separator = ";"; private String includenamespaceprefixes; private String avtXXFormsUsername; private String resolvedXXFormsUsername; private String avtXXFormsPassword; private String resolvedXXFormsPassword; public XFormsModelSubmission(XFormsContainingDocument containingDocument, String id, Element submissionElement, XFormsModel model) { this.containingDocument = containingDocument; this.id = id; this.submissionElement = submissionElement; this.model = model; // Extract event handlers eventHandlers = XFormsEventHandlerImpl.extractEventHandlers(containingDocument, this, submissionElement); } public XFormsContainingDocument getContainingDocument() { return containingDocument; } public Element getSubmissionElement() { return submissionElement; } private void extractSubmissionElement() { if (!submissionElementExtracted) { avtAction = submissionElement.attributeValue("action"); method = submissionElement.attributeValue("method"); method = Dom4jUtils.qNameToexplodedQName(Dom4jUtils.extractAttributeValueQName(submissionElement, "method")); validate = !"false".equals(submissionElement.attributeValue("validate")); relevant = !"false".equals(submissionElement.attributeValue("relevant")); version = submissionElement.attributeValue("version"); if (submissionElement.attributeValue("indent") != null) { indent = Boolean.valueOf(submissionElement.attributeValue("indent")).booleanValue(); } mediatype = submissionElement.attributeValue("mediatype"); encoding = submissionElement.attributeValue("encoding"); if (submissionElement.attributeValue("omitxmldeclaration") != null) { omitxmldeclaration = Boolean.valueOf(submissionElement.attributeValue("omit-xml-declaration")).booleanValue(); } if (submissionElement.attributeValue("standalone") != null) { standalone = new Boolean(submissionElement.attributeValue("standalone")); } cdatasectionelements = submissionElement.attributeValue("cdata-section-elements"); if (submissionElement.attributeValue("replace") != null) { replace = submissionElement.attributeValue("replace"); if (replace.equals("instance")) { replaceInstanceId = XFormsUtils.namespaceId(containingDocument, submissionElement.attributeValue("instance")); } } if (submissionElement.attributeValue("separator") != null) { separator = submissionElement.attributeValue("separator"); } includenamespaceprefixes = submissionElement.attributeValue("includenamespaceprefixes"); // Extension: username and password avtXXFormsUsername = submissionElement.attributeValue(XFormsConstants.XXFORMS_USERNAME_QNAME); avtXXFormsPassword = submissionElement.attributeValue(XFormsConstants.XXFORMS_PASSWORD_QNAME); // Remember that we did this submissionElementExtracted = true; } } private boolean isMethodOptimizedLocalSubmission() { return method.startsWith(XMLUtils.buildExplodedQName(XFormsConstants.XXFORMS_NAMESPACE_URI, "")) && (XFormsSubmissionUtils.isGet(method) || XFormsSubmissionUtils.isPost(method) || XFormsSubmissionUtils.isPut(method)); } public String getId() { return id; } public LocationData getLocationData() { return (LocationData) submissionElement.getData(); } public XFormsEventHandlerContainer getParentContainer() { return model; } public List getEventHandlers() { return eventHandlers; } public void performDefaultAction(PipelineContext pipelineContext, XFormsEvent event) { final String eventName = event.getEventName(); if (XFormsEvents.XFORMS_SUBMIT.equals(eventName) || XFormsEvents.XXFORMS_SUBMIT.equals(eventName)) { // 11.1 The xforms-submit Event // Bubbles: Yes / Cancelable: Yes / Context Info: None containingDocument.setGotSubmission(true); boolean isDeferredSubmissionSecondPass = false; try { // Make sure submission element info is extracted extractSubmissionElement(); final boolean isReplaceAll = replace.equals(XFormsConstants.XFORMS_SUBMIT_REPLACE_ALL); final boolean isReplaceInstance = replace.equals(XFormsConstants.XFORMS_SUBMIT_REPLACE_INSTANCE); final boolean isHandlingOptimizedGet = XFormsUtils.isOptimizeGetAllSubmission() && XFormsSubmissionUtils.isGet(method) && isReplaceAll; //noinspection UnnecessaryLocalVariable final boolean isDeferredSubmission = isReplaceAll; final boolean isDeferredSubmissionFirstPass = isDeferredSubmission && XFormsEvents.XFORMS_SUBMIT.equals(eventName) && !isHandlingOptimizedGet; isDeferredSubmissionSecondPass = isDeferredSubmission && !isDeferredSubmissionFirstPass; // Select node based on ref or bind final XFormsControls xformsControls = containingDocument.getXFormsControls(); xformsControls.setBinding(pipelineContext, submissionElement); // TODO FIXME: the submission element is not a control... final Node currentNode = xformsControls.getCurrentSingleNode(); if (currentNode == null) throw new OXFException("Empty single-node binding on xforms:submission for submission id: " + id); // Evaluate AVTs resolvedAction = XFormsUtils.resolveAttributeValueTemplates(pipelineContext, xformsControls, submissionElement, avtAction); resolvedXXFormsUsername = XFormsUtils.resolveAttributeValueTemplates(pipelineContext, xformsControls, submissionElement, avtXXFormsUsername); resolvedXXFormsPassword = XFormsUtils.resolveAttributeValueTemplates(pipelineContext, xformsControls, submissionElement, avtXXFormsPassword); if (!(currentNode instanceof Document || currentNode instanceof Element)) { throw new OXFException("xforms:submission: single-node binding must refer to a document node or an element."); } final XFormsInstance currentInstance = xformsControls.getCurrentInstance(); final Document initialDocumentToSubmit; if (!isDeferredSubmissionSecondPass) { // Create document to submit final Document backupInstanceDocument = currentInstance.getInstanceDocument(); try { initialDocumentToSubmit = createDocumentToSubmit(currentNode, currentInstance); currentInstance.setInstanceDocument(initialDocumentToSubmit, false); // Revalidate instance containingDocument.dispatchEvent(pipelineContext, new XFormsRevalidateEvent(model, false)); // TODO: The "false" attribute is no longer used. The above will cause events to be // sent out. Check if the validation state can really change. If so, find a // solution. // "no notification events are marked for dispatching due to this operation" // Check that there are no validation errors final boolean instanceSatisfiesValidRequired = isDocumentSatisfiesValidRequired(initialDocumentToSubmit); if (!instanceSatisfiesValidRequired) { // { // currentInstance.readOut(); // } if (logger.isDebugEnabled()) { final LocationDocumentResult documentResult = new LocationDocumentResult(); final TransformerHandler identity = TransformerUtils.getIdentityTransformerHandler(); identity.setResult(documentResult); currentInstance.read(identity); final String documentString = Dom4jUtils.domToString(documentResult.getDocument()); logger.debug("XForms - instance document or subset thereof cannot be submitted:\n" + documentString); } throw new OXFException("xforms:submission: instance to submit does not satisfy valid and/or required model item properties."); } } finally { currentInstance.setInstanceDocument(backupInstanceDocument, false); } } else { initialDocumentToSubmit = null; } // Deferred submission: end of the first pass if (isDeferredSubmissionFirstPass) { // When replace="all", we wait for the submission of an XXFormsSubmissionEvent from the client containingDocument.setClientActiveSubmission(this); return; } final Document documentToSubmit; if (isDeferredSubmissionSecondPass) { // Handle uploaded files if any final Element filesElement = (event instanceof XXFormsSubmissionEvent) ? ((XXFormsSubmissionEvent) event).getFilesElement() : null; if (filesElement != null) { for (Iterator i = filesElement.elements().iterator(); i.hasNext();) { final Element parameterElement = (Element) i.next(); final String name = parameterElement.element("name").getTextTrim(); final Element valueElement = parameterElement.element("value"); final String value = valueElement.getTextTrim(); // An empty value likely means that the user did not select a file. In this case, we don't // want to override an existing value in the instance. Clearing the value in the instance // will have to be done by other means, like a "clear file" button. if (value.length() == 0) continue; final String paramValueType = Dom4jUtils.qNameToexplodedQName(Dom4jUtils.extractAttributeValueQName(valueElement, XMLConstants.XSI_TYPE_QNAME)); final String filename = parameterElement.element("filename").getTextTrim(); final String mediatype = parameterElement.element("content-type").getTextTrim(); final String size = parameterElement.element("content-length").getTextTrim(); final UploadControlInfo uploadControl = (UploadControlInfo) containingDocument.getObjectById(pipelineContext, name); if (uploadControl != null) { // in case of xforms:repeat, the name of the template will not match an existing control // Set value into the instance xformsControls.setBinding(pipelineContext, uploadControl); { final Node currentSingleNode = xformsControls.getCurrentSingleNode(); XFormsInstance.setValueForNode(pipelineContext, currentSingleNode, value, paramValueType); } // Handle filename if any if (uploadControl.getFilenameElement() != null) { xformsControls.pushBinding(pipelineContext, uploadControl.getFilenameElement()); final Node currentSingleNode = xformsControls.getCurrentSingleNode(); XFormsInstance.setValueForNode(pipelineContext, currentSingleNode, filename, null); xformsControls.popBinding(); } // Handle mediatype if any if (uploadControl.getMediatypeElement() != null) { xformsControls.pushBinding(pipelineContext, uploadControl.getMediatypeElement()); final Node currentSingleNode = xformsControls.getCurrentSingleNode(); XFormsInstance.setValueForNode(pipelineContext, currentSingleNode, mediatype, null); xformsControls.popBinding(); } // Handle file size if any if (uploadControl.getSizeElement() != null) { xformsControls.pushBinding(pipelineContext, uploadControl.getSizeElement()); final Node currentSingleNode = xformsControls.getCurrentSingleNode(); XFormsInstance.setValueForNode(pipelineContext, currentSingleNode, size, null); xformsControls.popBinding(); } } } } // Create document to submit final Document backupInstanceDocument = currentInstance.getInstanceDocument(); try { documentToSubmit = createDocumentToSubmit(currentNode, currentInstance); currentInstance.setInstanceDocument(documentToSubmit, false); // Revalidate instance containingDocument.dispatchEvent(pipelineContext, new XFormsRevalidateEvent(model, false)); // TODO: The "false" attribute is no longer used. The above will cause events to be // sent out. Check if the validation state can really change. If so, find a // solution. // "no notification events are marked for dispatching due to this operation" // Check that there are no validation errors final boolean instanceSatisfiesValidRequired = isDocumentSatisfiesValidRequired(documentToSubmit); if (!instanceSatisfiesValidRequired) { // currentInstance.readOut();// FIXME: DEBUG throw new OXFException("xforms:submission: instance to submit does not satisfy valid and/or required model item properties."); } } finally { currentInstance.setInstanceDocument(backupInstanceDocument, false); } } else { // Don't recreate document documentToSubmit = initialDocumentToSubmit; } // Fire xforms-submit-serialize containingDocument.dispatchEvent(pipelineContext, XFormsEventFactory.createEvent(XFormsEvents.XFORMS_SUBMIT_SERIALIZE,XFormsModelSubmission.this)); // Serialize // To support: application/xml, application/x-www-form-urlencoded, multipart/related, multipart/form-data final byte[] serializedInstance; final String serializedInstanceString; { if (XFormsSubmissionUtils.isPost(method) || XFormsSubmissionUtils.isPut(method)) { try { final Transformer identity = TransformerUtils.getIdentityTransformer(); TransformerUtils.applyOutputProperties(identity, "xml", version, null, null, encoding, omitxmldeclaration, standalone, indent, 4); // TODO: use cdata-section-elements final ByteArrayOutputStream os = new ByteArrayOutputStream(); identity.transform(new DocumentSource(documentToSubmit), new StreamResult(os)); serializedInstance = os.toByteArray(); } catch (Exception e) { throw new OXFException("xforms:submission: exception while serializing instance to XML.", e); } serializedInstanceString = null; } else if (XFormsSubmissionUtils.isGet(method) || XFormsSubmissionUtils.isDelete(method)) { // Perform "application/x-www-form-urlencoded" serialization serializedInstanceString = createWwwFormUrlEncoded(documentToSubmit); serializedInstance = null; } else if (method.equals("multipart-post")) { // TODO throw new OXFException("xforms:submission: submission method not yet implemented: " + method); } else if (method.equals("form-data-post")) { // TODO throw new OXFException("xforms:submission: submission method not yet implemented: " + method); } else if (method.equals("urlencoded-post")) { throw new OXFException("xforms:submission: deprecated submission method requested: " + method); } else { throw new OXFException("xforms:submission: invalid submission method requested: " + method); } } final ExternalContext externalContext = (ExternalContext) pipelineContext.getAttribute(PipelineContext.EXTERNAL_CONTEXT); // Result information ConnectionResult connectionResult = null; try { if (isReplaceInstance && resolvedAction.startsWith("test:")) { // Test action if (serializedInstance == null) throw new OXFException("Action 'test:' can only be used with POST method."); connectionResult = new ConnectionResult(null); connectionResult.resultCode = 200; connectionResult.resultHeaders = new HashMap(); connectionResult.resultMediaType = "application/xml"; connectionResult.dontHandleResponse = false; connectionResult.resultInputStream = new ByteArrayInputStream(serializedInstance); } else if (isHandlingOptimizedGet) { // GET with replace="all": we can optimize and tell the client to just load the URL connectionResult = doOptimizedGet(pipelineContext, serializedInstanceString); } else if (!NetUtils.urlHasProtocol(resolvedAction) && (externalContext.getRequest().getContainerType().equals("portlet") || (externalContext.getRequest().getContainerType().equals("servlet") && (XFormsUtils.isOptimizeLocalSubmission() || isMethodOptimizedLocalSubmission()) && isReplaceAll))) { // This is an "optimized" submission, i.e. one that does not use an actual // protocol handler to access the resource // NOTE: Optimizing with include() for servlets doesn't allow detecting // errors caused by the included resource, so we don't allow this for now. // NOTE: For portlets, paths are served directly by the portlet, NOT as // resources. // Current limitations: // o Portlets cannot access resources outside the portlet except by using absolute URLs // o Servlets cannot access resources on the same serer but not in the current application // except by using absolute URLs final URI resolvedURI = XFormsUtils.resolveURI(submissionElement, resolvedAction); connectionResult = XFormsSubmissionUtils.doOptimized(pipelineContext, externalContext, this, method, resolvedURI.toString(), mediatype, isReplaceAll, serializedInstance, serializedInstanceString); } else { // This is a regular remote submission going through a protocol handler // Absolute URLs or absolute paths are allowed to a local servlet final String resolvedURL = XFormsUtils.resolveURL(containingDocument, pipelineContext, submissionElement, false, resolvedAction); connectionResult = XFormsSubmissionUtils.doRegular(pipelineContext, externalContext, method, resolvedURL, resolvedXXFormsUsername, resolvedXXFormsPassword, mediatype, isReplaceAll, serializedInstance, serializedInstanceString); } if (!connectionResult.dontHandleResponse) { // Handle response if (connectionResult.resultCode >= 200 && connectionResult.resultCode < 300) {// accept any success code (in particular "201 Resource Created") // Sucessful response final boolean hasContent; { if (connectionResult.resultInputStream == null) { hasContent = false; } else { if (!connectionResult.resultInputStream.markSupported()) connectionResult.resultInputStream = new BufferedInputStream(connectionResult.resultInputStream); connectionResult.resultInputStream.mark(1); hasContent = connectionResult.resultInputStream.read() != -1; connectionResult.resultInputStream.reset(); } } if (hasContent) { // There is a body if (isReplaceAll) { // When we get here, we are in a mode where we need to send the reply // directly to an external context, if any. // "the event xforms-submit-done is dispatched" containingDocument.dispatchEvent(pipelineContext, new XFormsSubmitDoneEvent(XFormsModelSubmission.this)); final ExternalContext.Response response = externalContext.getResponse(); // Forward headers to response if (connectionResult.resultHeaders != null) { for (Iterator i = connectionResult.resultHeaders.entrySet().iterator(); i.hasNext();) { final Map.Entry currentEntry = (Map.Entry) i.next(); final String headerName = (String) currentEntry.getKey(); final List headerValues = (List) currentEntry.getValue(); if (headerName != null && headerValues != null) { for (Iterator j = headerValues.iterator(); j.hasNext();) { response.addHeader(headerName, (String) j.next()); } } } } // Forward content to response NetUtils.copyStream(connectionResult.resultInputStream, response.getOutputStream()); } else if (isReplaceInstance) { final ByteArrayOutputStream resultByteArrayOutputStream = new ByteArrayOutputStream(); NetUtils.copyStream(connectionResult.resultInputStream, resultByteArrayOutputStream); byte[] submissionResponse = resultByteArrayOutputStream.toByteArray(); if (ProcessorUtils.isXMLContentType(connectionResult.resultMediaType)) { // Handling of XML media type try { final Transformer identity = TransformerUtils.getIdentityTransformer(); final LocationDocumentResult documentResult = new LocationDocumentResult(); identity.transform(new StreamSource(new ByteArrayInputStream(submissionResponse)), documentResult); final Document resultingInstanceDocument = documentResult.getDocument(); // Set new instance document to replace the one submitted final XFormsInstance replaceInstance = (replaceInstanceId == null) ? currentInstance : model.getInstance(replaceInstanceId); if (replaceInstance == null) { containingDocument.dispatchEvent(pipelineContext, new XFormsBindingExceptionEvent(XFormsModelSubmission.this)); } else { // Set new instance replaceInstance.setInstanceDocument(resultingInstanceDocument, true); // Mark all values as changed so that refresh sends appropriate events XFormsUtils.markAllValuesChanged(replaceInstance.getInstanceDocument()); // Handle new instance and associated events model.handleNewInstanceDocuments(pipelineContext); // Notify that submission is done containingDocument.dispatchEvent(pipelineContext, new XFormsSubmitDoneEvent(XFormsModelSubmission.this)); } } catch (Exception e) { throw new OXFException("xforms:submission: exception while serializing XML to instance.", e); } } else { // Other media type throw new OXFException("Body received with non-XML media type for replace=\"instance\": " + connectionResult.resultMediaType); } } else if (replace.equals(XFormsConstants.XFORMS_SUBMIT_REPLACE_NONE)) { // Just notify that processing is terminated containingDocument.dispatchEvent(pipelineContext, new XFormsSubmitDoneEvent(XFormsModelSubmission.this)); } else { throw new OXFException("xforms:submission: invalid replace attribute: " + replace); } } else { // There is no body, notify that processing is terminated containingDocument.dispatchEvent(pipelineContext, new XFormsSubmitDoneEvent(XFormsModelSubmission.this)); } } else if (connectionResult.resultCode == 302 || connectionResult.resultCode == 301) { // Got a redirect final ExternalContext.Response response = externalContext.getResponse(); // Forward headers to response // TODO: this is duplicated from above if (connectionResult.resultHeaders != null) { for (Iterator i = connectionResult.resultHeaders.entrySet().iterator(); i.hasNext();) { final Map.Entry currentEntry = (Map.Entry) i.next(); final String headerName = (String) currentEntry.getKey(); final List headerValues = (List) currentEntry.getValue(); if (headerName != null && headerValues != null) { for (Iterator j = headerValues.iterator(); j.hasNext();) { response.addHeader(headerName, (String) j.next()); } } } } // Forward redirect response.setStatus(connectionResult.resultCode); } else { // Error code received throw new OXFException("Error code received when submitting instance: " + connectionResult.resultCode); } } } finally { // Clean-up if (connectionResult != null) { connectionResult.close(); } } } catch (Throwable e) { if (isDeferredSubmissionSecondPass && XFormsUtils.isOptimizePostAllSubmission()) { // It doesn't serve any purpose here to dispatch an event, so we just propagate the exception throw new OXFException(e); } else { // Any exception will cause an error event to be dispatched containingDocument.dispatchEvent(pipelineContext, new XFormsSubmitErrorEvent(XFormsModelSubmission.this, resolvedAction, e)); } } } else if (XFormsEvents.XFORMS_BINDING_EXCEPTION.equals(eventName)) { // The default action for this event results in the following: Fatal error. throw new OXFException("Binding exception."); } } private ConnectionResult doOptimizedGet(PipelineContext pipelineContext, String serializedInstanceString) { final String actionString = resolvedAction + ((resolvedAction.indexOf('?') == -1) ? "?" : "") + serializedInstanceString; final String resultURL = XFormsActionInterpreter.resolveLoadValue(containingDocument, pipelineContext, submissionElement, true, actionString, null, null); final ConnectionResult connectionResult = new ConnectionResult(resultURL); connectionResult.dontHandleResponse = true; return connectionResult; } private Document createDocumentToSubmit(final Node currentNode, final XFormsInstance currentInstance) { // "A node from the instance data is selected, based on attributes on the submission // element. The indicated node and all nodes for which it is an ancestor are considered for // the remainder of the submit process. " final Document documentToSubmit; if (currentNode instanceof Element) { // Create subset of document documentToSubmit = Dom4jUtils.createDocument((Element) currentNode); } else { // Use entire instance document documentToSubmit = Dom4jUtils.createDocument(currentInstance.getInstanceDocument().getRootElement()); } if (relevant) { // "Any node which is considered not relevant as defined in 6.1.4 is removed." final Node[] nodeToDetach = new Node[1]; do { // NOTE: This is not very efficient, but at least we avoid NPEs that we would get by // detaching elements within accept(). Should implement a more efficient algorithm to // prune non-relevant nodes. nodeToDetach[0] = null; documentToSubmit.accept(new VisitorSupport() { public final void visit(Element element) { checkInstanceData(element); } public final void visit(Attribute attribute) { checkInstanceData(attribute); } private final void checkInstanceData(Node node) { if (nodeToDetach[0] == null) { final InstanceData instanceData = XFormsUtils.getInstanceDataUpdateInherited(node); // Check "relevant" MIP and remove non-relevant nodes { final BooleanModelItemProperty relevantMIP = instanceData.getInheritedRelevant(); if (relevantMIP != null && !relevantMIP.get()) nodeToDetach[0] = node; } } } }); if (nodeToDetach[0] != null) nodeToDetach[0].detach(); } while (nodeToDetach[0] != null); } // TODO: handle includenamespaceprefixes return documentToSubmit; } private String createWwwFormUrlEncoded(final Document document) { final StringBuffer sb = new StringBuffer(); document.accept(new VisitorSupport() { public final void visit(Element element) { // We only care about elements final List children = element.elements(); if (children == null || children.size() == 0) { // Only consider leaves final String text = element.getText(); if (text != null && text.length() > 0) { // Got one! final String localName = element.getName(); if (sb.length() > 0) sb.append(separator); try { sb.append(URLEncoder.encode(localName, "utf-8")); sb.append('='); sb.append(URLEncoder.encode(text, "utf-8")); // TODO: check if line breaks will be correcly encoded as "%0D%0A" } catch (UnsupportedEncodingException e) { // Should not happen: utf-8 must be supported throw new OXFException(e); } } } } }); return sb.toString(); } private boolean isDocumentSatisfiesValidRequired(final Document documentToSubmit) { final boolean[] instanceSatisfiesValidRequired = new boolean[]{true}; if (validate) { documentToSubmi |
Administrator
|
Adrian,
> Attached is the patch for xforms-submit-serialize. The only > uncertainty I had was whether I needed to include the context > information described in the spec in XFormsSubmitSerializeEvent > (currently it records nothing but the target). Thanks. In fact I don't understand what the spec means (or why it means what I think it means) when it says "Context Info: A node into which data to be submitted can be placed." I sounds like you could capture the event and then with xforms:setvalue, modify the data, but I am not sure. For now I suggest we leave things as they are, anyway we don't have the event() function yet. > It works well as a solution: I just use the event to set a @validated > flag in an instance document which is used to decide between a "there > was a backend error" or "your data is invalid" type of message: Sounds great :-) > Of course, it would be easier if there were two types of > xforms-submit-error events! With 1.1 it will be possible to obtain information from events using the event() function. So at the very least there will be a mechanism in place, and at best the complete solution will be in 1.1. Before I commit those changes, I just need to be sure that you give us the right to include the content in the OPS code base under LGPL. Ideally, this should be with copyright assignation to Orbeon as well, even though you obviously keep the copyright on your own code as well. Since the code is fairly simple I hope this is not an issue. -Erik -- Orbeon - XForms Everywhere: http://www.orbeon.com/blog/ -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help ObjectWeb mailing lists service home page: http://www.objectweb.org/wws |
Adrian Erik Bruchez wrote: Adrian, -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help ObjectWeb mailing lists service home page: http://www.objectweb.org/wws |
Administrator
|
Great, that's committed now. I will try to add an example of this real soon.
Thanks again for a great contribution! -Erik Adrian Baker wrote: > Sorry my Eclipse template settings mistakenly introduced my company > copyright template - you can remove that and replace it with the > standard Orbeon copyright & LGPL license. > > Adrian > > > Erik Bruchez wrote: >> Adrian, >> >> > Attached is the patch for xforms-submit-serialize. The only >> > uncertainty I had was whether I needed to include the context >> > information described in the spec in XFormsSubmitSerializeEvent >> > (currently it records nothing but the target). >> >> Thanks. In fact I don't understand what the spec means (or why it >> means what I think it means) when it says "Context Info: A node into >> which data to be submitted can be placed." >> >> I sounds like you could capture the event and then with >> xforms:setvalue, modify the data, but I am not sure. For now I suggest >> we leave things as they are, anyway we don't have the event() function >> yet. >> >> > It works well as a solution: I just use the event to set a @validated >> > flag in an instance document which is used to decide between a "there >> > was a backend error" or "your data is invalid" type of message: >> >> Sounds great :-) >> >> > Of course, it would be easier if there were two types of >> > xforms-submit-error events! >> >> With 1.1 it will be possible to obtain information from events using >> the event() function. So at the very least there will be a mechanism >> in place, and at best the complete solution will be in 1.1. >> >> Before I commit those changes, I just need to be sure that you give us >> the right to include the content in the OPS code base under LGPL. >> >> Ideally, this should be with copyright assignation to Orbeon as well, >> even though you obviously keep the copyright on your own code as >> well. Since the code is fairly simple I hope this is not an issue. >> >> -Erik Orbeon - XForms Everywhere: http://www.orbeon.com/blog/ -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help ObjectWeb mailing lists service home page: http://www.objectweb.org/wws |
Administrator
|
All,
The DMV Forms example now features an example of the use of xforms-submit-serialize. We have also added a blog entry to illustrate how to use this event: http://www.orbeon.com/blog/2006/06/16/xforms-tip-differentiating-between-submit-errors/ Note that we have now implemented conditional actions as per the XForms 1.1 draft, which makes it easier to display a different error message when receiving xforms-submit-error. Enjoy, -Erik Erik Bruchez wrote: > Great, that's committed now. I will try to add an example of this real > soon. > > Thanks again for a great contribution! > > -Erik > > Adrian Baker wrote: >> Sorry my Eclipse template settings mistakenly introduced my company >> copyright template - you can remove that and replace it with the >> standard Orbeon copyright & LGPL license. >> >> Adrian >> >> >> Erik Bruchez wrote: >>> Adrian, >>> >>> > Attached is the patch for xforms-submit-serialize. The only >>> > uncertainty I had was whether I needed to include the context >>> > information described in the spec in XFormsSubmitSerializeEvent >>> > (currently it records nothing but the target). >>> >>> Thanks. In fact I don't understand what the spec means (or why it >>> means what I think it means) when it says "Context Info: A node into >>> which data to be submitted can be placed." >>> >>> I sounds like you could capture the event and then with >>> xforms:setvalue, modify the data, but I am not sure. For now I suggest >>> we leave things as they are, anyway we don't have the event() function >>> yet. >>> >>> > It works well as a solution: I just use the event to set a @validated >>> > flag in an instance document which is used to decide between a "there >>> > was a backend error" or "your data is invalid" type of message: >>> >>> Sounds great :-) >>> >>> > Of course, it would be easier if there were two types of >>> > xforms-submit-error events! >>> >>> With 1.1 it will be possible to obtain information from events using >>> the event() function. So at the very least there will be a mechanism >>> in place, and at best the complete solution will be in 1.1. >>> >>> Before I commit those changes, I just need to be sure that you give us >>> the right to include the content in the OPS code base under LGPL. >>> >>> Ideally, this should be with copyright assignation to Orbeon as well, >>> even though you obviously keep the copyright on your own code as >>> well. Since the code is fairly simple I hope this is not an issue. >>> >>> -Erik > -- Orbeon - XForms Everywhere: http://www.orbeon.com/blog/ -- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: mailto:[hidden email] For general help: mailto:[hidden email]?subject=help ObjectWeb mailing lists service home page: http://www.objectweb.org/wws |
Free forum by Nabble | Edit this page |