distinguishing between frontend/backend xforms-submit-error ?

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

distinguishing between frontend/backend xforms-submit-error ?

Adrian Baker-2
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
Reply | Threaded
Open this post in threaded view
|

Re: distinguishing between frontend/backend xforms-submit-error ?

Adrian Baker-2
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
Reply | Threaded
Open this post in threaded view
|

Re: Re: distinguishing between frontend/backend xforms-submit-error ?

Alessandro  Vernet
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
Reply | Threaded
Open this post in threaded view
|

Re: Re: distinguishing between frontend/backend xforms-submit-error ?

Adrian Baker-2
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,

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: [hidden email]
For general help: [hidden email]
ObjectWeb mailing lists service home page: http://www.objectweb.org/wws






-- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: [hidden email] For general help: [hidden email] ObjectWeb mailing lists service home page: http://www.objectweb.org/wws


/**
 *  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
Reply | Threaded
Open this post in threaded view
|

Re: Re: distinguishing between frontend/backend xforms-submit-error ?

Erik Bruchez
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
Reply | Threaded
Open this post in threaded view
|

Re: Re: distinguishing between frontend/backend xforms-submit-error ?

Adrian Baker-2
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


-- You receive this message as a subscriber of the [hidden email] mailing list. To unsubscribe: [hidden email] For general help: [hidden email] ObjectWeb mailing lists service home page: http://www.objectweb.org/wws



--
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
Reply | Threaded
Open this post in threaded view
|

Re: Re: distinguishing between frontend/backend xforms-submit-error ?

Erik Bruchez
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
Reply | Threaded
Open this post in threaded view
|

Re: Re: distinguishing between frontend/backend xforms-submit-error ?

Erik Bruchez
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