Today's question from Karl is about WebDAV. He's asking about viewing emails stored in Nuxeo via WebDAV. There's actually two questions here. First one is how do you get the content of an msg file and second one is how do you take control of Document creation via WebDAV.


How to view email stored via WebDav How to view email stored via WebDav

When you map a drive to Nuxeo using WebDAV, each new file you create there will be created as a File Document in Nuxeo. If you want complete control on how the document is created, you need to implement your own backend. Or at least extend the existing one and override the create file method. Here's how you do it:

[xml]
<extension target="org.nuxeo.ecm.platform.wi.service.WIPluggableBackendManager" point="backendFactory">
<backendFactory name="CustomBackend"
class="org.nuxeo.webdav.CustomBackend" />
</extension>
[/xml]


There are some constraints due to the WebDAV model that you have to take into account. The major one is the naming of your document. It must be exactly the same as the hard drive file. It makes the usual FileManager import method useless as it does not necessarily set the right name.

So you need to put your custom document creation logic in the backend. The goal here is to visualize the content of the mail using the default JSF interface. So what we have to do is to create a MailMessage document:

[java]
package org.nuxeo.ecm.platform.wi.backend;

import org.nuxeo.ecm.core.api.Blob;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.blobholder.BlobHolder;

public class CustomBackend extends SimpleBackend {

protected CustomBackend(String backendDisplayName, String rootPath,
        String rootUrl) {
    super(backendDisplayName, rootPath, rootUrl);
}

@Override
public DocumentModel createFile(String parentPath, String name, Blob content)
        throws ClientException {
    DocumentModel parent = resolveLocation(parentPath);
    if (!parent.isFolder()) {
        throw new ClientException(
                &quot;Can not create a child in a non folderish node&quot;);
    }
    String targetType = &quot;File&quot;;
    // name = cleanName(name);
    try {

        if (content != null
                &amp;&amp; &quot;image/vnd.fpx&quot;.equals(content.getMimeType())) {
            targetType = &quot;MailMessage&quot;;
        }

        cleanTrashPath(parent, name);
        DocumentModel doc = getSession().createDocumentModel(
                parent.getPathAsString(), name, targetType);
        doc.setPropertyValue(&quot;dc:title&quot;, name);

        if (content != null) {
            if (&quot;image/vnd.fpx&quot;.equals(content.getMimeType())) {
                setMailMessageMetadata(doc, content);
            } else {
                BlobHolder bh = doc.getAdapter(BlobHolder.class);
                if (bh != null) {
                    bh.setBlob(content);
                }
            }
        }
        doc = getSession().createDocument(doc);
        getPathCache().put(parseLocation(parentPath) + &quot;/&quot; + name, doc);
        return doc;
    } catch (Exception e) {
        discardChanges();
        throw new ClientException(&quot;Error child creating new folder&quot;, e);
    }
}

@Override
public DocumentModel updateDocument(DocumentModel doc, String name,
        Blob content) throws ClientException {
    if (&quot;image/vnd.fpx&quot;.equals(content.getMimeType())) {
        try {
            setMailMessageMetadata(doc, content);
        } catch (Exception e) {
            throw new ClientException(e);
        }
    } else {
        BlobHolder bh = doc.getAdapter(BlobHolder.class);
        if (bh != null) {
            bh.setBlob(content);
        }
    }
    doc.putContextData(SOURCE_EDIT_KEYWORD, &quot;webdav&quot;);
    getSession().saveDocument(doc);
    saveChanges();
    return doc;
}

}
[/java]

Now that we have control of the document creation, we can work on the msg parsing and extraction. Apache poi-scratchpad will help you do that. It's already in Nuxeo so there is no need for a new library. Here's what you can do with this:

[java]
private DocumentModel setMailMessageMetadata(DocumentModel doc, Blob content)
throws Exception {
MAPIMessage msg = new MAPIMessage(content.getStream());
msg.getSubject();
msg.getTextBody();

    String subject = msg.getSubject();
    String sender = msg.getDisplayFrom();

    String[] recipients = msg.getDisplayTo().split(&quot;,&quot;);
    String[] ccRecipients = msg.getDisplayCC().split(&quot;,&quot;);

    List&lt;Blob&gt; attachments = extractAttachment(msg);
    String text = msg.getTextBody();

    doc.setPropertyValue(&quot;dc:title&quot;, subject);
    doc.setPropertyValue(SENDER_PROPERTY_NAME, sender);
    doc.setPropertyValue(RECIPIENTS_PROPERTY_NAME, recipients);
    doc.setPropertyValue(CC_RECIPIENTS_PROPERTY_NAME, ccRecipients);
    if (attachments != null &amp;&amp; !attachments.isEmpty()) {
        ArrayList&lt;Map&lt;String, Serializable&gt;&gt; files = new ArrayList&lt;Map&lt;String, Serializable&gt;&gt;();
        for (Blob currentFileBlob : attachments) {
            if (currentFileBlob != null) {
                Map&lt;String, Serializable&gt; file = new HashMap&lt;String, Serializable&gt;();
                file.put(&quot;file&quot;, (Serializable) currentFileBlob);
                file.put(&quot;filename&quot;, currentFileBlob.getFilename());
                files.add(file);
            }
        }
        doc.setPropertyValue(&quot;files:files&quot;, files);
    }
    doc.setPropertyValue(CC_RECIPIENTS_PROPERTY_NAME, ccRecipients);

    StringBlob sb = new StringBlob(text);
    BlobHolder simpleBlobHolder = new SimpleBlobHolder(sb);
    ConversionService conversionService = Framework.getService(ConversionService.class);
    Map&lt;String, Serializable&gt; parameters = new HashMap&lt;String, Serializable&gt;();
    parameters.put(&quot;tagFilter&quot;, &quot;body&quot;);
    BlobHolder simpleTextBH = conversionService.convert(&quot;html2text&quot;,
            simpleBlobHolder, parameters);
    String simpleText = simpleTextBH.getBlob().getString();
    doc.setPropertyValue(TEXT_PROPERTY_NAME, simpleText);
    doc.setPropertyValue(HTML_TEXT_PROPERTY_NAME, text);
    return doc;
}

private List&lt;Blob&gt; extractAttachment(MAPIMessage msg) throws IOException {
    List&lt;Blob&gt; attachments = new ArrayList&lt;Blob&gt;();
    Map attachmentMap = msg.getAttachmentFiles();
    if (attachmentMap.size() &gt; 0) {
        for (Iterator iterator = attachmentMap.entrySet().iterator(); iterator.hasNext();) {
            Map.Entry entry = (Map.Entry) iterator.next();
            String attachmentfilename = entry.getKey().toString();

            // extract attachment
            ByteArrayInputStream fileIn = (ByteArrayInputStream) entry.getValue();
            Blob b = new InputStreamBlob(fileIn);
            b.setFilename(attachmentfilename);
            attachments.add(b);
            b.persist();
            fileIn.close();
        }
    }
    return attachments;
}

[/java]

That's some really quick and dirty code, but you get the idea :) You might also extract much more information from the msg if you use a newer version of POI than the one we currently have in Nuxeo.