XForms(Orbeon), REST, CMIS and Alfresco – part 2 Files

To keep things simple this example uses basic authentication – to see how to handle authentication in more detail see part 1 here

This is a relatively straightforward example of how to upload a file into Alfresco from Orbeon using CMIS over REST.

I’m not going to cover the details of how the REST interface works (as usual POST for create, PUT for update) or go into depth about how to use CMIS as this is covered elsewhere e.g. ECM Architect article on CMIS

Starting from the standard example of how to upload a file in Orbeon the changes to make are:

  • Instead of posting a multi-part mime message by using the method form-data-post use a normal post and encode the file using xxforms:doc-base64
  • Use the test: (echo: in 3.9) action – after this has been done the file will be uploaded to the server in a temporary directory
  • Create a second submission to post the file to Alfresco

An alternative way to encode the file would be to use the technique described here with a serialized element replacing the media element. This is simpler but less instructive.

For the second submission I use a template file containing a blank CMIS entry and fill in the appropriate fields, you can populate any aspect data fields here. I’ve either used xforms:setvalue or xslt as part of an xpl pipeline depending on what I’m doing – here I’m keeping it simple and just using setvalue.

The contents of the xforms:dispatch element are used to control the parameters to set as part of the upload submission (you’ll see that the file is sent to a test directory in this example) – there are a very limited set of parameters here but it’s easy enough to add more.

(I haven’t done any error handling although you can see what to do using the ins-cmis-rest-control instance)

Gotchas

  • According to the atom spec the contents of the atom:content are treated differently according to the value of the type attribute – it’s therefore safer to use cmisra:content instead
  • If there’s an error in Alfresco, e.g. trying to create two files with the same name, then Alfresco will return text/html not text/xml even if you ask it nicely.

It’s worth noting that if you try and POST the same file twice you will get a 500 response – if you try and upload the same file twice via Share then you get a second file with the suffix (1)

This covers the basics of how to upload a file from Orbeon to Alfresco using the CMIS REST interface.

Updating a file, using PUT or deleting are fairly straightforward after this. (Note that the metadata and file contents are different things so are updated separately.)

Enough talking – here’s the example

 <html xmlns:xforms="http://www.w3.org/2002/xforms"
    xmlns:f="http://orbeon.org/oxf/xml/formatting"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:xxforms="http://orbeon.org/oxf/xml/xforms"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:xxi="http://orbeon.org/oxf/xml/xinclude"
    xmlns:ev="http://www.w3.org/2001/xml-events"
    xmlns:atom="http://www.w3.org/2005/Atom"
    xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
    xmlns:xi="http://www.w3.org/2001/XInclude"
      xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/"
    xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/"
    xmlns:alf="http://www.alfresco.org">

<head>

<title>Spike Upload</title>

<xforms:model id="cmis-rest-model">
    <xxforms:variable name="alfresco-uri"
        select="xxforms:property('alfresco.uri')"
        as="xs:anyURI" />
    <xxforms:variable name="alfresco-username"
        select="xxforms:property('alfresco.username')"
        as="xs:string" />
    <xxforms:variable name="alfresco-credentials"
        select="xxforms:property('alfresco.credentials')"
        as="xs:string" />
    <xforms:instance id="ins-cmis-rest-control">
        <control>
            <status />
        </control>
    </xforms:instance>

    <xforms:instance id="ins-cmis-rest-create-file">
        <xi:include href="create-file.xml" xxi:omit-xml-base="true" />
    </xforms:instance>
    <xforms:instance id="ins-cmis-rest-create-file-result">
        <atom:entry />
    </xforms:instance>
    <xforms:instance id="ins-upload">
        <upload xmlns="">
            <summary />
            <media filename="" mediatype="" size=""
                                            xsi:type="xs:anyURI" />
            <category>term="foo"; scheme="bar"; label="baz"</category>
        </upload>
    </xforms:instance>

    <xforms:instance id="ins-upload-template">
        <upload xmlns="">
            <summary />
            <media filename="" mediatype="" size=""
                                            xsi:type="xs:anyURI" />
            <category>term="foo"; scheme="bar"; label="baz"</category>
        </upload>
    </xforms:instance>

    <xforms:submission id="sub-background-upload"
        ref="instance('ins-upload')" method="post" replace="none"
        resource="test:" validate="false" />

    <xforms:submission
                id="sub-form-data-post"
                method="post"
                action="test:"
                ref="instance('ins-upload')"
                replace="instance"
                instance="ins-post-response">

        <xforms:header>
            <xforms:name>Accept</xforms:name>
            <xforms:value>application/atom+xml</xforms:value>
        </xforms:header>

        <xforms:action ev:event="xforms-submit-done">
            <xxforms:variable name="media-entry"
                                   select="instance('ins-upload')"/>
            <xxforms:variable name="file-uri"
                             select="instance('ins-upload')/media"/>

            <xforms:dispatch target="cmis-rest-model"
                                       name="cmis-rest-upload-file">
                <xxforms:context name="fr:media-entry"
                                             select="$media-entry"/>
                <xxforms:context name="fr:media-title"
                            select="$media-entry//media/@filename"/>
                <xxforms:context name="fr:media-type"
                         select="$media-entry//atom:content/@type"/>

                <xxforms:context name="fr:file-content"    
                          select="xxforms:doc-base64($file-uri)" />

            </xforms:dispatch>
            <xforms:insert nodeset="instance('ins-upload')"
                         origin="instance('ins-upload-template')" />
            <xforms:send submission="sub-get-feed" />
        </xforms:action>
    </xforms:submission>

    <xforms:instance id="ins-post-response">
        <foo xmlns="" />
    </xforms:instance>

    <xforms:submission id="sub-get-feed" method="get"
        instance="ins-feed" serialization="none"
        resource="{$alfresco-uri}service/cmis/p/test/children"
        mediatype="application/atom+xml"
        xxforms:username="{$alfresco-username}"
        xxforms:password="{$alfresco-credentials}" replace="instance">
    </xforms:submission>

    <xforms:instance id="ins-feed">
        <atom:feed />
    </xforms:instance>

    <xforms:send ev:event="xforms-model-construct-done"
        submission="sub-get-feed" />

    <xforms:action ev:event="cmis-rest-upload-file">
        <xxforms:variable name="media-type"
                                   select="event('fr:media-type')"/>
        <xxforms:variable name="content"
                                 select="event('fr:file-content')"/>
        <xforms:delete
            nodeset="instance('ins-cmis-rest-create-file')/atom:entry/*" />
        <xforms:insert
            context="instance('ins-cmis-rest-create-file')/atom:entry"
            origin="instance('ins-cmis-rest-create-file-template')/atom:entry/*"
            at="last()" position="after" />

        <xforms:setvalue
            ref="instance('ins-cmis-rest-create-file')//atom:title"
            value="event('fr:media-title')" />

        <xforms:setvalue
            ref="instance('ins-cmis-rest-create-file')
                                     //cmisra:content/cmisra:base64"
            value="$content" />

        <xforms:setvalue
            ref="instance('ins-cmis-rest-create-file')
                                  //cmisra:content/cmisra:mediatype"
            value="$media-type" />

        <xforms:setvalue
            ref="instance('ins-cmis-rest-create-file')
//cmis:propertyString
    [@propertyDefinitionId='cmis:contentStreamMimeType']/cmis:value"
            value="$media-type" />

        <!-- You can set any values you like here
               e.g. a custom document type or aspect properties -->
        <xforms:send submission="cmis-rest-create-file-submission" />

    </xforms:action>
    <xforms:submission ref="instance('ins-cmis-rest-create-file')"
        id="cmis-rest-create-file-submission" method="post"
        action="{$alfresco-uri}service/cmis/p/test/children"
        mediatype="application/atom+xml"
        xxforms:username="{$alfresco-username}"
        xxforms:password="{$alfresco-credentials}" replace="instance"
        instance="ins-cmis-rest-create-file-result">
        <!-- This doesn't actually help for errors,
                    e.g. 2 files with the same name, as Alfresco
              always returns a 500 with Content-Type: text/html  -->
        <xforms:header>
            <xforms:name>Accept</xforms:name>
            <xforms:value>text/xml</xforms:value>
        </xforms:header>
        <xforms:action ev:event="xforms-submit-error">
            <xforms:setvalue
                ref="instance('ins-cmis-rest-control')//status"
                value="'fail'" />
            <xforms:message level="modal"
                value="concat('An error occurred while creating
                 a file in Alfresco. Please inform an administrator.
                         (error-type:',event('error-type'), ')')" />
        </xforms:action>
        <xforms:action ev:event="xforms-submit-done">
    <xforms:setvalue ref="instance('ins-cmis-rest-control')//status"
                value="'ok'" />
        </xforms:action>
    </xforms:submission>
</xforms:model>

</head>
<body>

    <h1>Spike Upload</h1>

    <xforms:group ref="instance('ins-upload')">

        <p>

            <!-- Upload field -->
            <xforms:upload ref="media" xxforms:size="60">
                <xforms:label>Upload file: </xforms:label>
                <xforms:filename ref="@filename" />
                <xforms:mediatype ref="@mediatype" />
                <xxforms:size ref="@size" />
            </xforms:upload>

            <xforms:output value="media" appearance="xxforms:download">
                <xforms:label>Download</xforms:label>
                <xforms:filename ref="@filename" />
                <xforms:mediatype ref="@mediatype" />
            </xforms:output>

        </p>

        <p>
            <xforms:textarea ref="summary">
                <xforms:label>Summary: </xforms:label>
            </xforms:textarea>
        </p>

    </xforms:group>

    <p>
        <xforms:submit submission="sub-form-data-post">
            <xforms:label>Submit (form-data-post)</xforms:label>
        </xforms:submit>
    </p>

    <hr />

    <fr:datatable paginated="false">
        <thead>
            <tr>
                <th fr:sortable="true" fr:resizeable="true">File Name</th>
                <th fr:sortable="true" fr:resizeable="true">Published</th>
                <th fr:sortable="true" fr:resizeable="true">Author</th>
                <th>Summary</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            <xforms:repeat id="rep-media"
                nodeset="instance('ins-feed')/atom:entry">
                <tr>
                    <td><xforms:output ref="atom:title" /></td>
                    <td><xforms:output ref="atom:published"
                            xxforms:format="format-dateTime(., '[Y0001]-[M01]-[D01]')" /></td>
                    <td><xforms:output ref="atom:author/atom:email" /></td>
                    <td><xforms:output ref="atom:summary" /></td>
                    <td></td>
                </tr>
            </xforms:repeat>
        </tbody>
    </fr:datatable>

</body>
</html>

The template file:

 <?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom"
    xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/"
    xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/"
    xmlns:alf="http://www.alfresco.org">
    <title>sample-a.doc</title>
    <summary>A sample whitepaper named Sample A</summary>
    <cmisra:content>
        <cmisra:mediatype></cmisra:mediatype>
        <cmisra:base64></cmisra:base64>
    </cmisra:content>
    <cmisra:object>
        <cmis:properties>
            <cmis:propertyId
                propertyDefinitionId="cmis:objectTypeId">
                         <cmis:value>cmis:document</cmis:value>
            </cmis:propertyId>
            <cmis:propertyString
                propertyDefinitionId="cmis:contentStreamMimeType">
                     <cmis:value>text/xml</cmis:value>
            </cmis:propertyString>
            <alf:setAspects>
                <alf:appliedAspects>P:cm:titled</alf:appliedAspects>
                <alf:appliedAspects>P:rn:renditioned</alf:appliedAspects>
                <alf:appliedAspects>P:cm:taggable</alf:appliedAspects>
                <alf:appliedAspects>P:cm:templatable</alf:appliedAspects>
                <alf:appliedAspects>P:cm:author</alf:appliedAspects>
                <alf:appliedAspects>P:app:inlineeditable</alf:appliedAspects>
                <alf:properties>
                    <cmis:propertyString
                            propertyDefinitionId="cm:author"
                            displayName="Author"
                            queryName="cm:author">
                           <cmis:value></cmis:value>
                     </cmis:propertyString>
                    <cmis:propertyString
                            propertyDefinitionId="cm:title"
                            displayName="Title"
                            queryName="cm:title">
                           <cmis:value></cmis:value>
                    </cmis:propertyString>
                </alf:properties>
            </alf:setAspects>
        </cmis:properties>
    </cmisra:object>
</entry>

 

3 Replies to “XForms(Orbeon), REST, CMIS and Alfresco – part 2 Files”

  1. I’ve tryied to replicate your example in orbeon 4.4 and alfresco 4.2d , unfortunately it fails to add content after file is created in alfresco.
    All I have is a 0 byte file in the test directory, can you help me to sort it out?

    Carlos

  2. Hi,
    I haven’t been working in this area for a while now and the versions of orbeon and alfresco are a fair bit later than I was using – that being said…

    Is there anything showing in either the Alfresco or Orbeon logs?

    It sounds like the create file submission is OK but cmis-rest-upload-file has not worked

Leave a Reply

Your email address will not be published. Required fields are marked *