[Monday dev heaven] Sharing with the outside world #2: getting your readers' feedback


Mon 27 February 2012 By Laurent Doguin


Add and list comments on a document Add and list comments on a document

The real life example I had in mind last time was about writing and publishing job offers. Staying on the sharing/collaborating trend, we're going to give the opportunity to users to not only read the job offer but to send feedback to the application. It's a common need for such a thing. We could easily imagine an application form at the bottom of the page that would collect applications, enter them in Nuxeo with an adhoc document type including resume and cover letter, adhoc content lifecycle including all the required steps (filtering, review, notifications...) up to selections of candidates. But this is a whole HR application and I don’t want to lose you. We will just make a simple thing here for now. We will allow users to simply comment on job offers to illustrate the idea of collecting user-generated feedback, content or documents. Then I'll show you how to display the list of comments.

Adding a comment


We're going to add a new method on our webengine module which will handle the post. All the annotation are usual JAX-RS. First part of the method is similar to what we already did. It gets the document identified by the uid given in the url. Then we use the document adapters. There is one for the commentable document. The new CommentableDocument object gives us the method we need to list or add comments.

[java]
@POST
@Path("{uid}/addComment")
public Object addComment(@PathParam("uid") String uid,
@FormParam("text") String text, @FormParam("author") String author,
@FormParam("title") String title) throws ClientException {
// Get the context coreSession
CoreSession coreSession = ctx.getCoreSession();
// Create a documentRef from the uid given in the URL
DocumentRef documentRef = new IdRef(uid);
// Fetch the document using the coreSession and the DocumentRef
DocumentModel document = coreSession.getDocument(documentRef);
// if the document does not exist
if (document == null) {
Response.status(Status.NOT_FOUND).build();
}
CommentableDocument commentableDoc = document
.getAdapter(CommentableDocument.class);
if (commentableDoc != null) {
// Create the comment document and add it to the current doc
DocumentModel comment = coreSession.createDocumentModel("Comment");
comment.setProperty("dublincore", "title", title);
comment.setProperty("comment", "author", author);
comment.setProperty("comment", "text", text);
comment.setProperty("comment", "creationDate",
Calendar.getInstance());
commentableDoc.addComment(comment);
}
// get the list of document to show them on the front end
List<DocumentModel> comments = getDocumentComment(document);
// Return the freemarker view with document and comments as arguments.
return getView("index").arg("document", document).arg("comments",
comments);
}

private List&lt;DocumentModel&gt; getDocumentComment(DocumentModel doc)
        throws ClientException {
    CommentableDocument commentableDoc = doc
            .getAdapter(CommentableDocument.class);
    if (commentableDoc == null) {
        return new ArrayList&lt;DocumentModel&gt;();
    }
    List&lt;DocumentModel&gt; comments = commentableDoc.getComments();
    return comments;
}

[/java]

The corresponding form looks like this:

[xml]
<div>
<p>Add a comment:</p>
<form action="${This.path}/${document.id}/addComment" method="post" enctype="application/x-www-form-urlencoded" name="addComment">
<div>
<div>Author: <input type="text" name="author" value="${author}" /></div>
<div>Title: <input type="text" name="title" value="${title}" /></div>
<div><textArea class="commentText"
name="text" value="${text}" > </textArea></div>
<div><input type="submit" value="Post" name="Post" class="button" /></div>
</div>
</form>
</div>
[/xml]

And we have everything we need to add comments to our document.

Display the comments


Now we need to display those comments. We can get them using the adapter and pass them as arguments of the view so that we can display them with the freemarker template, exactly like we did with the document. We'll name our new variable 'comments'. It's a list of document so we'll have to use the freemarker list directive. Here's what you need to add in the freemarker template:

[xml]
<div class="commentList">
<p>Comments:</p>
<#list comments as comment>
<div class="commentBox">
<div><h5>${comment["dc:title"]}</h5></div>
<div class="comment">
<div>${comment["comment:text"]}</div>
<div class="commentSignature">Written by ${comment["comment:author"]}, ${comment["comment:creationDate"]}</div>
</div>
</div>
</#list>
</div>
[/xml]

Now what about those css class? If you've used the IDE to create the project, you should have a site.css file under resources/skin/resources/css . It's already loaded in our page because of resources/skin/base.ftl . This file is the main template of our page. This is where we define the title, the resources, meta, link etc...

[xml]
<html>
<head>
<title>
<@block name="title">
${document.title}
</@block>
</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<link rel="stylesheet" href="${skinPath}/css/site.css" type="text/css" media="screen" charset="utf-8">
<link rel="shortcut icon" href="${skinPath}/image/favicon.gif" />
<@block name="stylesheets" />
<@block name="header_scripts" />
</head>

<body style="margin:0px 0px 0px 0px;">
<div id="fb-root"></div>
<script>
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js#xfbml=1";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>

<table class="main">
<tr>
<td>
<table class="header">
<tr>
<td><img src="${skinPath}/img/logo.png"</td>
<td align="right"><@block name="header">The Header</@block></td>
</tr>
</table>
</td>
</tr>
<tr height="98%">
<td valign="top"><@block name="content">The Content</@block></td>
</tr>
</table/>

</body>
</html>
[/xml]

You might not be familiar with @block. This is what let us use template in webengine instead of doing everything in the same file. Here's an extract from our documentation:

WebEngine defines also a template model that is used to build responses. The template engine is based on FreeMarker, plus some custom extensions like template blocks. Using blocks you can build your web site in a modular fashion. Blocks are dynamic template parts that can be extended or replaced using derived blocks. Using blocks, you can write a base template that may define the site layout (using blocks containing empty or generic content) and then write final skins for your layout by extending the base template and redefining blocks you are interested in.

So as you can see it's really quick and easy to define a classic website using Nuxeo. You don't necessarily have to know JSF or our Theme engine.


Category: Product & Development
Tagged: Java, Monday Dev Heaven, Nuxeo IDE