Menu

Xforms submit error with multiple submissions : submission-in-progress

Help
2019-12-19
2020-01-13
  • Josselin Morvan

    Josselin Morvan - 2019-12-19

    Hi everyone

    We are developing an app with xforms (with XSLTForms client) and BaseX, to manage historical entities (actors) and conduct network analysis. In one of our forms, we fill the relations between an actor and his relatives, and we'd like, when submitting the form, to create and submit a pre-filled instance for each relative…

    …but we have issues with the multiple submissions.

    Basically, we have 3 instances :

    instance('entity') for the main form

    <xf:instance id="entity">
        <entity xmlns="">
            <name>John Doe</name>
            <age>35</age>
            <address>North Cap</address>
            <relations>
                <relation ref="" type="sibling">Elisa Doe</relation>
                <relation ref="" type="parent">Richard Doe</relation>
            </relations>
        </entity>
    </xf:instance>
    

    instance('newEntity'), filled and sent successively for each relation in instance('entity')(for the example we just copy the name value)

    <xf:instance id="newEntity">
        <entity xmlns="">
            <name/>
            <age/>
            <address/>
            <relations>
                <relation ref="" type=""/>
            </relations>
        </entity>
    </xf:instance>
    

    instance('iterate'), for looping the submissions of instance('newEntity') (when instance('iterate') = 1, we process relation[position() = 1], etc

    <xf:instance id="iterate">
        <iterate xmlns=""/>
    </xf:instance>
    

    And here is our submit process :

    First, we initiate the loop with a trigger, while there is more relation than the value of instance('iterate')we submit the instance('newEntity') ; and when all the relations have been sent, we submit the main form.

    <xf:trigger>
        <xf:label>submit</xf:label>
        <xf:action ev:event="DOMActivate">
            <xf:setvalue ref="instance('iterate')" value="1"/>
            <xf:setvalue ref="instance('newEntity')/name" value="instance('entity')/relations/relation[position() = instance('iterate')]"/>
            <xf:action while="count(//relation) &gt;= instance('iterate')">
                <xf:send submission="submitNewEntity"/>
                <xf:action if="count(//relation) &lt; instance('iterate')">
                    <xf:send submission="submitEntity"/>
                </xf:action>
            </xf:action>
        </xf:action>
    </xf:trigger>
    

    Then we have the submission for the "new entities". Nothing special here, when the submit is done, we set the value of relation/@ref corresponding to the last submission thanks to the response of the server, then we add "+1" to the instance('iterate')for the loop.

    <xf:submission ref="instance('newEntity')" id="submitNewEntity" resource="/test/form/put" method="put" replace="none">
        <xf:action ev:event="xforms-submit-done">
            <xf:message level="modal">
                OK 
                Status : <xf:output value="event('response-status-code')"/>; 
                URI : <xf:output value="event('resource-uri')"/>; 
                Headers : <xf:output value="event('response-headers')"/>; 
                Reason : <xf:output value="event('response-reason-phrase')"/>;
                Body : <xf:output value="event('response-body')"/>.
            </xf:message>
            <xf:setvalue value="event('response-body')//*:id" ref="instance('entity')//relation[position() = instance('iterate')]/@ref"/>
            <xf:setvalue ref="instance('iterate')" value="instance('iterate') + 1"/>
        <xf:setvalue ref="instance('newEntity')/name" value="instance('entity')/relations/relation[position() = instance('iterate')]"/>
        </xf:action>
        <xf:action ev:event="xforms-submit-error">
            <xf:message level="modal">
                Error : <xf:output value="event('error-type')"/>; 
                Status : <xf:output value="event('response-status-code')"/>; 
                URI : <xf:output value="event('resource-uri')"/>; 
                Headers : <xf:output value="event('response-headers')"/>; 
                Reason : <xf:output value="event('response-reason-phrase')"/> 
                Body : <xf:output value="event('response-body')"/>.
            </xf:message>
        </xf:action>
    </xf:submission>
    

    And finally we are submitting the main form when all the relations have been saved

    <xf:submission ref="instance('entity')" id="submitEntity" resource="/test/form/put" method="put" replace="none">
        <xf:action ev:event="xforms-submit-done">
            <xf:message level="modal">
                OK ! 
                Status : <xf:output value="event('response-status-code')"/>; 
                URI : <xf:output value="event('resource-uri')"/>; 
                Headers : <xf:output value="event('response-headers')"/>; 
                Reason : <xf:output value="event('response-reason-phrase')"/>;
                Body : <xf:output value="event('response-body')"/>.
            </xf:message>
        </xf:action>
        <xf:action ev:event="xforms-submit-error">
            <xf:message level="modal">
                Error : <xf:output value="event('error-type')"/>; 
                Status : <xf:output value="event('response-status-code')"/>; 
                URI : <xf:output value="event('resource-uri')"/>; 
                Headers : <xf:output value="event('response-headers')"/>; 
                Reason : <xf:output value="event('response-reason-phrase')"/> 
                Body : <xf:output value="event('response-body')"/>.
            </xf:message>
        </xf:action>
    </xf:submission>
    

    Everything is working great except that before each submission of instance('newEntity'), we have a submission error with an error message : "submission-in-progress".

    In fact with this example, and I don't know why, we have 5 submissions instead of 3 :

    • 2 for the relation 'Elisa Doe' (the first fails, the second is OK)
    • 2 for the relation 'Richard Doe' (the first fails, the second is OK)
    • 1 for John Doe (the "main" form)

    We don't know how to avoid that and we are asking for some help, if someone has an idea ?

    Below are the two complete files.

    The form :

    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/xforms" xmlns:ev="http://www.w3.org/2001/xml-events">
        <head>
            <title>Submission</title>
            <xf:model>
                <xf:instance id="entity">
                    <entity xmlns="">
                        <name>John Doe</name>
                        <age>35</age>
                        <address>North Cap</address>
                        <relations>
                            <relation ref="" type="sibling">Elisa Doe</relation>
                            <relation ref="" type="parent">Richard Doe</relation>
                        </relations>
                    </entity>
                </xf:instance>
    
                <xf:instance id="newEntity">
                    <entity xmlns="">
                        <name/>
                        <age/>
                        <address/>
                        <relations>
                            <relation ref="" type=""/>
                        </relations>
                    </entity>
                </xf:instance>
    
                <xf:instance id="iterate">
                    <iterate xmlns=""/>
                </xf:instance>
    
                <xf:submission ref="instance('entity')" id="submitEntity" resource="/test/form/put" method="put" replace="none">
                    <xf:action ev:event="xforms-submit-done">
                        <xf:message level="modal">
                            OK ! 
                            Status : <xf:output value="event('response-status-code')"/>; 
                            URI : <xf:output value="event('resource-uri')"/>; 
                            Headers : <xf:output value="event('response-headers')"/>; 
                            Reason : <xf:output value="event('response-reason-phrase')"/>;
                            Body : <xf:output value="event('response-body')"/>.
                        </xf:message>
                    </xf:action>
                    <xf:action ev:event="xforms-submit-error">
                        <xf:message level="modal">
                            Error : <xf:output value="event('error-type')"/>; 
                            Status : <xf:output value="event('response-status-code')"/>; 
                            URI : <xf:output value="event('resource-uri')"/>; 
                            Headers : <xf:output value="event('response-headers')"/>; 
                            Reason : <xf:output value="event('response-reason-phrase')"/> 
                            Body : <xf:output value="event('response-body')"/>.
                        </xf:message>
                    </xf:action>
                </xf:submission>
    
                <xf:submission ref="instance('newEntity')" id="submitNewEntity" resource="/test/form/put" method="put" replace="none">
                    <xf:action ev:event="xforms-submit-done">
                        <xf:message level="modal">
                            OK 
                            Status : <xf:output value="event('response-status-code')"/>; 
                            URI : <xf:output value="event('resource-uri')"/>; 
                            Headers : <xf:output value="event('response-headers')"/>; 
                            Reason : <xf:output value="event('response-reason-phrase')"/>;
                            Body : <xf:output value="event('response-body')"/>.
                        </xf:message>
                        <xf:setvalue value="event('response-body')//*:id" ref="instance('entity')//relation[position() = instance('iterate')]/@ref"/>
                        <xf:setvalue ref="instance('iterate')" value="instance('iterate') + 1"/>
                    </xf:action>
                    <xf:action ev:event="xforms-submit-error">
                        <xf:message level="modal">
                            Error : <xf:output value="event('error-type')"/>; 
                            Status : <xf:output value="event('response-status-code')"/>; 
                            URI : <xf:output value="event('resource-uri')"/>; 
                            Headers : <xf:output value="event('response-headers')"/>; 
                            Reason : <xf:output value="event('response-reason-phrase')"/> 
                            Body : <xf:output value="event('response-body')"/>.
                        </xf:message>
                    </xf:action>
                </xf:submission>
    
            </xf:model>
        </head>
        <body>
            <xf:input ref="name">
                <xf:label>name : </xf:label>
            </xf:input>
            <br />
            <xf:input ref="age">
                <xf:label>age : </xf:label>
            </xf:input>
            <br />
            <xf:input ref="address">
                <xf:label>address : </xf:label>
            </xf:input>
            <br />
            <xf:repeat nodeset="relations/relation" id="repeatRelation">
                <xf:input ref=".">
                    <xf:label>Name : </xf:label>
                </xf:input>
                <xf:select1 ref="@type">
                    <xf:item>
                        <xf:label>parent</xf:label>
                        <xf:value>parent</xf:value>
                    </xf:item>
                    <xf:item>
                        <xf:label>sibling</xf:label>
                        <xf:value>sibling</xf:value>
                    </xf:item>
                </xf:select1>
                <xf:trigger>
                    <xf:label>X</xf:label>
                    <xf:action ev:event="DOMActivate">
                        <xf:delete ref="." at="1" if="count(//relation) > 1"/>
                    </xf:action>
                </xf:trigger>
            </xf:repeat>
            <xf:trigger>
                <xf:label>Add relation</xf:label>
                <xf:action ev:event="DOMActivate">
                    <xf:insert nodeset="relations/relation" at="last()"/>
                    <xf:setvalue ref="relations/relation[index('repeatRelation')] | relations/relation[index('repeatRelation')]/@*"/>
                </xf:action>
            </xf:trigger>
            <br/>
            <xf:trigger>
                <xf:label>submit</xf:label>
                <xf:action ev:event="DOMActivate">
                    <xf:setvalue ref="instance('iterate')" value="1"/>
                    <xf:setvalue ref="instance('newEntity')/name" value="instance('entity')/relations/relation[position() = instance('iterate')]"/>
                    <xf:action while="count(//relation) &gt;= instance('iterate')">
                        <xf:send submission="submitNewEntity"/>
                        <xf:action if="count(//relation) &lt; instance('iterate')">
                            <xf:send submission="submitEntity"/>
                        </xf:action>
                    </xf:action>
                </xf:action>
            </xf:trigger>
        </body>
    </html>
    

    The Xquery requests for BaseX

    xquery version "3.0";
    module namespace test = "test";
    
    declare namespace rest = "http://exquery.org/ns/restxq";
    declare namespace file = "http://expath.org/ns/file";
    declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
    declare namespace web = "http://basex.org/modules/web";
    declare namespace update = "http://basex.org/modules/update";
    declare namespace db = "http://basex.org/modules/db";
    
    declare namespace xf = "http://www.w3.org/2002/xforms";
    declare namespace ev = "http://www.w3.org/2001/xml-events";
    
    declare default element namespace "test";
    declare default function namespace "test";
    
    declare default collation "http://basex.org/collation?lang=fr";
    
    declare variable $test:xsltFormsPath := "/test/files/xsltforms/xsltforms.xsl";
    
    
    (:~
     : this function defines a static files directory for the app
     :
     : @param $file file or unknown path
     : @return binary file
     :)
    declare
    %rest:path('test/files/{$file=.+}')
    function test:file($file as xs:string) as item()+ {
        let $path := file:base-dir() || 'files/' || $file
        return
            (
            web:response-header( map {'media-type' : web:content-type($path)}),
            file:read-binary($path)
            )
    };
    
    (:~
     : install db
     : @return create the db
     :
     : @todo create the prosopo db
     :)
    declare 
      %rest:path("/test/install")
      %output:method("xml")
      %updating
    function install() {
      if (db:exists("test")) 
        then (
          update:output("db 'test' already exists !")
         )
        else (
          update:output("db 'test' has been created"),
          db:create( "test", <root/>, "test.xml")
          )
    };
    
    
    (:~
     : This function returns db "test"
     :
     :)
    declare
      %rest:path("/test")
      %output:method("xml")
    function opendb() {
      db:open('test')
    };
    
    
    (:~
     : This function returns the entity form
     :
     :)
    declare
      %rest:path("test/form")
      %output:method("xml")
    function test() {
      fn:doc('files/submission.xml')
    };
    
    (:~
     : This function sends entity instance to db "test"
     : an @xml:id is created and sent to the form as http response
     : @param $param content
     :)
    
     declare
    %rest:path("test/form/put")
    %output:method("xml")
    %rest:header-param("Referer", "{$referer}", "none")
    %rest:PUT("{$param}")
    %updating
    function entityPut($param, $referer) {
      let $db := db:open("test")
      return 
        let $id := 'person' || fn:format-integer(fn:count($db/*:root/*:entity) + 1, '0000')
        let $param :=
          copy $d := $param
          modify insert node attribute xml:id {$id} into $d/*
          return $d
        return (
          insert node $param into $db/*:root,
          update:output(
             (
              <rest:response>
                <http:response status="200" message="">
                  <http:header name="Content-Language" value="fr"/>
                  <http:header name="Content-Type" value="text/plain; charset=utf-8"/>
                </http:response>
              </rest:response>,
              <result>
                <id>{$id}</id>
              </result>
             )
           )
        )
    };
    

    Thanks a lot for your help !!

     
  • Alain Couthures

    Alain Couthures - 2019-12-24

    Hello Josselin,

    It is currently not possible with XSLTForms to put the same submission more than once in asynchronous mode while the previous one is not yet finished. As a workaround, I suggest you to add "mode='synchronous'" to your submission declarations.

    Thank you for your feedback!

    --Alain

     

    Last edit: Alain Couthures 2019-12-24
  • Josselin Morvan

    Josselin Morvan - 2020-01-13

    Hello Alain,

    Thank you very much for your help, with "mode='synchronous'" it works perfectly !
    I missed this feature.

    Thank you again,
    Josselin

     

Log in to post a comment.