What would be a social feature without any user interaction ? Nothing…
It’s why during this step, we’ll add two very original social feature to our JuZcret application: A user can comment and "like" a secret… Crazy ! :D
By implementing these new features we will learn amongst other how to use Ajax with Juzu, interact with Portlet Container or add an EDIT mode to our Portlet.

Let’s start by improving a little bit the application models.

Models

Here no Juzu Stuff, we will just improve our current model. Lets add the top of the tree, Model. This class contains common attributes for Secret and Comment:

Class Diagram

In org.juzu.tutorial.models package create Model class as below:

package org.juzu.tutorial.models;

import java.io.Serializable;
import java.util.Date;

public class Model implements Serializable {
    private String id;
    private Date createdDate;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }
}

Then Comment class:

package org.juzu.tutorial.models;

public class Comment extends Model {
    private String userId;
    private String content;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

We also need to improve Secret:

  1. Make Secret extends Model class to inherits the parent attributes.

  2. A Secret object will contains several Comment relationship, and several like (that consist of a simple list of userId):

package org.juzu.tutorial.models;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

public class Secret extends Model {

    private String message;

    private String imageURL;

    private Set<String> likes;

    private List<Comment> comments;

    public Secret() {
        likes = new HashSet<String>();
        comments = new LinkedList<Comment>();
    }

    public Set<String> getLikes() {
        Set<String> lks = new HashSet<String>(likes);
        return lks;
    }

    public void setLikes(Set<String> likes) {
        this.likes = likes;
    }

    public List<Comment> getComments() {
        List<Comment> cms = new LinkedList<Comment>(comments);
        return cms;
    }

    public void setComments(List<Comment> comments) {
        this.comments = comments;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getImageURL() {
        return imageURL;
    }

    public void setImageURL(String imageURL) {
        this.imageURL = imageURL;
    }
}

That’s enough ! Our model is ready for comment and like feature. Now we need to improve the Secret service by providing an API to add comments and like secrets.

Improve Secret Service

Our Secret Service need two more methods to manage the new functionalities. One for adding comment to secret (addComment method) and an other one to like a secret (addLike method).

Similarly to step-2, data is still saved in memory for now.

Firstly, declare these two method in the SecretServie interface:

import org.juzu.tutorial.models.Comment;
...
import java.util.Set;

...

public Comment addComment(String secretId, Comment comment);

public Set<String> addLike(String secretId, String userId);

Then implement these 2 methods in the SecretServiceMemImpl and update the addSecret function:

import org.juzu.tutorial.models.Comment;
[...]
import java.util.*;
[...]

public void addSecret(String message, String imageUrl) {
    Secret secret = new Secret();
    secret.setId(UUID.randomUUID().toString());
    secret.setMessage(message);
    secret.setImageURL(imageUrl);
    secret.setCreatedDate(new Date());
    secretsList.add(secret);
  }

public Comment addComment(String secretId, Comment comment) {
        Secret secret = getSecret(secretId);
        if (secret != null) {
            comment.setId(UUID.randomUUID().toString());
            comment.setCreatedDate(new Date());

            List<Comment> comments = secret.getComments();
            comments.add(comment);
            secret.setComments(comments);
        }
        return comment;
    }

    public Set<String> addLike(String secretId, String userId) {
        Secret secret = getSecret(secretId);
        if (secret != null) {
            Set<String> likes = secret.getLikes();
            // You can like only one time
            if (!likes.contains(userId)) {
                likes.add(userId);
            }
            secret.setLikes(likes);
            return likes;
        }
        return null;
    }

    private Secret getSecret(String secretId) {
        Secret secret = null;
        for (Secret s : getSecrets()) {
            if (s.getId().equals(secretId)) {
                secret = s;
            }
        }
        return secret;
    }

    ...

Done for service layer !

This two methods are pretty simple and self-explained so we don’t have to spend time on it.

It’s time to go back to Juzu and improve the presentation layer.

Present like and comment

The comment and like action will be manage using Ajax via the @Ajax Juzu annotation from the Juzu Ajax plugin. The Ajax plugin like the Binding plugin is already included in Juzu-core, so no need to add new dependencies in our pom.

It’s important to know that the Juzu-Ajax plugin depends on jQuery. So it’s mandatory to declare jQuery ad we do in the previous step if we want to use this plugin.

Then you can use @Ajax in our controller. So lets add 2 new controller methods in JuZcretApplication.java:

import juzu.*;
import juzu.plugin.ajax.Ajax;
import juzu.request.SecurityContext;
import org.json.JSONArray;
import org.json.JSONObject;
import org.juzu.tutorial.models.Comment;
import org.juzu.tutorial.services.SecretService;

import javax.inject.Inject;
import java.security.Principal;
import java.util.Set;

...

  private static final String ANONYMOUS = "Anonymous";

  @Ajax
    @Resource
    public Response addComment(String secretId, @Mapped Comment comment, SecurityContext context) {
      comment.setUserId(getCurrentUser(context));
      Comment result = secretService.addComment(secretId, comment);
      if (result != null) {
        return Response.ok(new JSONObject(result).toString()).withMimeType("text/json");
      } else {
        return Response.status(503);
      }
    }

    @Ajax
    @Resource
    public Response addLike(String secretId, SecurityContext context) {
      Set<String> likes = secretService.addLike(secretId, getCurrentUser(context));
      if (likes != null) {
        return Response.ok(new JSONArray(likes).toString()).withMimeType("text/json");
      } else {
        return Response.status(503);
      }
    }

    private String getCurrentUser(SecurityContext context) {
        Principal user = context.getUserPrincipal();
        if (user == null) {
          return ANONYMOUS;
        } else {
          return user.getName();
        }
      }

@Ajax annotation comes from Juzu-Ajax plugin, it provide us convenient ajax calling method: jzLoad, jzAjax. We’ll use this later in secret.js.

@Resource is a new type of Controller. Resource controllers are pretty much like a view controller except that they must produce the entire response sent to the client and that is perfect for implementing ajax request.

@Mapped allow to map request parameter to Bean types. Juzu do automatically the conversion between the primary types and the request parameters but for a Bean, we need to declare it with @Mapped. Consequently the parameters of the add secret form will be automatically mapped to the attribute of the @Mapped Bean.

Juzu also injects automatically some contextual useful objects that you can use:

You just need to declare it in the method sign as we do above for SecurityContext and Juzu will inject them automatically at runtime.

You notice that we response a Json data to our client by declaring the MimeType to text/json. Now we need to handled this response on the client side.

Template

We need to add two new buttons to like a secret and comment a secret in the secretWall.gtmpl. Then for Comment feature we need also to display a popover to show the list of current comments and add a new comment.

Replace the current content of <ul class="secret-wall-list clearfix"> by this:

[...]
            <ul class="secret-wall-list clearfix">
                    <% secretsList.each { secret -> %>
                    <li class="secret" data-secretId="${secret.id}">
                        <div class="secret-image" style="background-image: url('${secret.imageURL}')">

                            <div class="secret-mesage">${secret.message}</div>

                            <div class="secret-action">
                                <a class="btn-like secr-toggle-link toggle-like-comment" href="#"><i
                                        class="uiIconThumbUp uiIconWhite"></i><span
                                        class="numb"></span></a>
                                <a class="btn-popup-comment secr-toggle-link toggle-write-comment" href="#"><i
                                        class="uiIconComment uiIconWhite"></i><span
                                        class="numb"></span></a>
                            </div>

                            <div class="popover popover-secret fade top">
                                <button class="closePopover close" type="button">&times;</button>
                                <div class="arrow"></div>

                                <div class="popover-content">
                                    <div class="secr-comments-box">
                                        <ul class="secr-comments-list">
                                            <% secret.getComments().each { comment -> %>
                                            <li><!--Add class .open-popover to display popover -->
                                                <div class="media">
                                                    <a class="pull-left" href="http://localhost:8080/portal/intranet/profile/${comment.userId}">
                                                        <img src="http://localhost:8080/social-resources/skin/images/ShareImages/UserAvtDefault.png"
                                                             alt="avatar">
                                                    </a>

                                                    <div class="media-body">
                                                        <div>
                                                            <a class="cm-user-name" href="http://localhost:8080/portal/intranet/profile/${comment.userId}">${comment.userId}</a> <span
                                                                class="cm-time">${comment.createdDate}</span>
                                                        </div>

                                                        <div class="cm-content">${comment.content}</div>
                                                    </div>
                                                </div>
                                            </li>
                                            <% } %>
                                        </ul>
                                    </div>
                                    <div class="secr-create-comment clearfix">
                                        <button class="btn-comment btn btn-primary pull-right">Comment</button>

                                        <div class="secr-write-comment ">
                                            <div class="inner">
                                                <div class="media">
                                                    <a href="#" class="pull-left"><img
                                                            src="http://localhost:8080/social-resources/skin/images/ShareImages/UserAvtDefault.png"
                                                            alt="avatar"></a>

                                                    <div class="media-body">
                                                        <textarea name="comment" class="secret-add-comment" placeholder="Add your comment"></textarea>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </li>
                    <% } %>
                </ul>

After that we need to improve our juzcret.less file to manage new added class. Update the existing less file with these:

//Variables
//====================

[...]

@secretActionHeight: 43px;

//Mixins
//====================

[...]

//Border Radius CSS3
.border-radius(@border-radius) {
  -webkit-border-radius: @border-radius;
  -moz-border-radius: @border-radius;
  -ms-border-radius: @border-radius; //IE9 only
  border-radius: @border-radius;
}
//Transform CSS3
.transform(@transform) {
  -webkit-transform: @transform;
  -moz-transform: @transform;
  -ms-transform: @transform; //IE9 only
  transform: @transform;
}
//Transitions CSS3
.transition(@transition) {
  -webkit-transition: @transition;
  -o-transition: @transition;
  transition: @transition;
}
//Translate CSS
.translate(@x; @y) {
  -webkit-transform: translate(@x, @y);
  -ms-transform: translate(@x, @y); //IE9 only
  -o-transform: translate(@x, @y);
  transform: translate(@x, @y);
}

//Common Style
//====================

[...]

//After secret-wall-heading, remove the secret-wall-list section and replace by:
//After secret-wall-heading, remove the secret-wall-list section and replace by:
.secret-wall-list {
  margin: 0 -@secretItemGutter;
  > li {
    float: left;
    padding: @secretItemGutter;
    width: 100% / 3;
    .secret-image {
      background-repeat: no-repeat;
      background-size: cover;
      background-color: #000;
      position: relative;
      height: @heightSecretItem;
      width: 100%;
      display: block;
      &:before {
        background: none repeat scroll 0 0 rgba(0, 0, 0, 0.5);
        content: "";
        display: block;
        height: 100%;
        position: absolute;
        width: 100%;
      }
    }
    .secret-mesage {
      bottom: 65px;
      color: #fff;
      font-size: 20px;
      font-weight: normal;
      left: 25px;
      line-height: 24px;
      position: absolute;
      right: 25px;
      text-align: center;
      top: 25px;
    }
    .secret-action {
      border-top: 1px solid rgba(255, 255, 255, 0.5);
      bottom: 0;
      height: 0;
      left: 0;
      line-height: @secretActionHeight;
      padding: 0 25px;
      position: absolute;
      right: 0;
      text-align: right;
      overflow: hidden;
      .transition(all 200ms ease-out 0s);

      .secr-toggle-link {
        + .secr-toggle-link {
          margin-left: 15px;
        }
        > i {
          margin-right: 5px;
        }
        .numb {
          color: #fff;
          font-size: 13px;
        }
        .uiIconComment {
          margin-top: 2px;
        }
      }
    }
    .popover {
      max-width: 500px;
      top: auto;
      bottom: 46px;
      left: auto;
      right: -205px;
      width: 500px;
      margin: 0px;
    }
    .close {
      line-height: 16px;
      padding: 1px 5px;
      position: absolute;
      right: 0;
      top: 0;
    }
    .media {
      > .pull-left {
        > img {
          width: 36px;
          height: 36px;
          .border-radius(2px);
        }
      }
    }
    &:hover, &.open-popover {
      .secret-action {
        height: @secretActionHeight;
      }
    }
    &.open-popover {
      .popover-secret {
        .opacity(1);
        display: block;
      }
    }
    &:nth-child(3n+3) {
      .popover{
        right: -1px;
        .arrow {
          left: auto;
          right: 34px;
        }
      }
    }
  }
}
.secret-popup {
  width: 500;
  height: 280px;
  background: #fff;
  border: 1px solid rgba(0, 0, 0, 0.5);
  display: none;
  &.in {
    display: block;
  }
}
.popover-secret {
  .popover-content {
    padding: 15px;
  }
}
.secr-comments-box {
  .secr-viewall {
    font-size: 13px;
    margin-bottom: 15px;
  }
}
.secr-comments-list {
  margin-bottom: 20px;
  max-height: 150px;
  overflow: auto;
  > li {
    line-height: 18px;
    + li {
      margin-top: 20px;
    }
    .media {
      > .pull-left {
        display: block;
      }
    }
    .cm-user-name {
      font-weight: bold;
    }
    .cm-time {
      color: #999999;
      font-size: 12px;
      margin-left: 5px;
    }
  }
}
.secr-create-comment {
  .btn-primary {
    float: right;
    margin-left: 10px;
    margin-top: 3px;
  }
  .secr-write-comment {
    .fluid-colum {
      float: left;
      width: 100%;
      > .inner {
        margin-left: 46px;
      }
    }
    .media {
      > .media-body {
        margin-left: 46px;
        padding-top: 3px;
      }
    }
    textarea {
      height: 29px;
      resize: none;
      width: 100%;
      &:focus {
        box-shadow:none;
      }
    }
  }
}

[...]
[…] means sections already added in step 3.

Now we have 2 buttons for comment and like features and a popover to display the list of comments.

Now that we finish the UI part, we need to add some js handlers to manage this two features using Ajax.

Javascript Handler

Update the secret.js file by adding the snippet in charge of the like feature:

(function ($) {

    $(document).ready(function () {

        [...]

    });

    //Ajax for managing like function
    $(document).on('click.juzu.secret.addLike', '.btn-like', function () {
        var jLike = $(this);
        var jSecret = jLike.closest('.secret');
        var secretId = jSecret.attr('data-secretId');

        jLike.jzAjax('JuZcretApplication.addLike()', {
            data: {'secretId': secretId},
            success: function (data) {
                var jLikeIcon = jSecret.find('.btn-like');
                jLikeIcon.find('.numb').text($(data).size());
            }
        });
        return false;
    });

})($);

This snippet register an event on our Like button. The interesting line to notice here is

jLike.jzAjax('JuZcretApplication.addLike()', [...]);

jzAjax and jzLoad functions are jQuery plugin provided by the Juzu Ajax plugin. They replace the standard Ajax and Load jQuery function. They accept the same arguments but the URL is replace by the controller method.
All we need is provide the controller method like JuZcretApplication.addLike() and Juzu take care to find the expected URL, and perform Ajax request (using jQuery).

Similarly, we also have another three JS listener for the comment feature. Add them just after the snippet which just added above:

    //Open the popover for displaying and adding comments
    $(document).on('click.juzu.secret.openPopover', '.btn-popup-comment', function () {
        var jComment = $(this);
        var jSecret = jComment.closest('.secret');
        jSecret.addClass('open-popover');
    });

    //Close the popover for displaying and adding comments
    $(document).on('click.juzu.secret.closePopover', '.closePopover', function () {
        var jComment = $(this);
        var jSecret = jComment.closest('.secret');
        jSecret.removeClass('open-popover');
    });

    //Ajax for managing comment function
    $(document).on('click.juzu.secret.addComment', '.btn-comment', function () {
        var jComment = $(this);
        var jSecret = jComment.closest('.secret');
        var secretId = jSecret.attr('data-secretId');

        jComment.jzAjax('JuZcretApplication.addComment()', {
            data: {'secretId': secretId, 'content': jSecret.find('.secret-add-comment').val()},
            success: function (data) {
                if (typeof(data) == 'string') {
                    //error response
                    alert(data);
                } else {
                    //update html
                    var cList = "";
                    var cCounter = 0;
                    $(data).each(function (idx, elem) {
                        if (elem.content) {
                            cList +=
                                "<div class='media'>" +
                                "<a class='pull-left' href='http://localhost:8080/portal/intranet/profile/" + elem.userId + "'>" +
                                "<img src='http://localhost:8080/social-resources/skin/images/ShareImages/UserAvtDefault.png' alt='avatar'>" +
                                "</a>" +
                                "<div class='media-body'>" +
                                "<div>" +
                                "<a class='cm-user-name' href='http://localhost:8080/portal/intranet/profile/" + elem.userId + "'>" + elem.userId + "</a> " +
                                "<span class='cm-time'>" + elem.createdDate + "</span>" +
                                "</div>" +
                                "<div class='cm-content'>" + elem.content + "</div>" +
                                "</div>" +
                                "</div>";
                            cCounter++;
                        }
                    });
                    var html = jSecret.find('.secr-comments-list').html();
                    jSecret.find('.secr-comments-list').html(html + cList);
                    var jCommentIcon = jSecret.find('.btn-popup-comment');
                    var jCommentNumb = jCommentIcon.find('.numb').text();
                    jCommentIcon.find('.numb').text(jCommentNumb+cCounter);
                }
            }
        });
        return false;
    });

There is one major difference in this second handler is that we handle error response. For which purpose ? To not allow user to submit empty comment.
It’s why our Juzu controller should be aware of invalid data that user submitted. Lets move to data validation and error handling provided by Juzu.

Adding validation

Juzu provides controller handler parameter validation via the Bean Validation framework. To use it we need to add the juzu-validation plugin in our pom.xml:

<dependency>
  <groupId>org.juzu</groupId>
  <artifactId>juzu-plugins-validation</artifactId>
  <version>1.0.0</version>
</dependency>

And now, all that we need is just adding annotation to model attribute. Update the Comment class as below:

package org.juzu.tutorial.models;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;

public class Comment extends Model {

  private String userId;
  @Pattern(regexp = "^.+$", message = "Comment content must not be empty")
  @NotNull(message = "Comment content is required")
  private String            content;

  [...]
}

Thanks to @Pattern and @NotNull annotation, the validation framework will validate the parameter and throw validation error if needed.

We need also to declare to perform this validation in the controller. In our case we want to validate new comment coming from user. This is managed by the addComment Resource in JuZcretApplication where we need to add the @Valid annotation to the Comment parameter:

import javax.validation.Valid;

[...]

  @Ajax
  @Resource
  public Response addComment(String secretId, @Mapped @Valid Comment comment, SecurityContext context) {
  [...]
  }

Now if a user try to enter an invalid comment, the validation framework will throw an error. Our job is not totally finish… We need also to cache properly this error.
Juzu provides 2 solutions for error handling:

  1. Using request lifecycle

  2. Using error handler

In our case we will use the request lifecycle that allow us to handle the error in the controller.
What we need is to analyze the Response and check if the type is ValidationError. If it is, we simply get the error message and update the response to send it properly to the client.
For doing this we need our controller JuZcretApplication to implement the interface RequestLifeCycle and override the endRequest method:

import juzu.plugin.validation.ValidationError;
import juzu.request.RequestContext;
import juzu.request.RequestLifeCycle;
[...]
import javax.validation.ConstraintViolation;

public class JuZcretApplication implements RequestLifeCycle {

      @Override
      public void endRequest(RequestContext context) {
          Response response = context.getResponse();
          if (response instanceof ValidationError) {
              ValidationError error = (ValidationError)response;
              Set<ConstraintViolation<Object>> violations = error.getViolations();

              String msg = violations.iterator().next().getMessage();
              response = Response.ok(msg).withMimeType("text/html");
              context.setResponse(response);
          }
      }

      @Override
      public void beginRequest(RequestContext context) {
      }
}

On the client side, we already implement our JS handler to display the error message:

      ...

      success: function(data) {
        if (typeof(data) == 'string') {
          //error response
          alert(data);
        } else {
             ...
        }

Our JuZcret app now provides pretty good feature for end user:

Like and comment feature
If you continue this step just after finishing the step 3, you just have to compile your project, paste the new created war in eXo Platform and start the server. Then access to your JuZcret page and take a look at the result. If not, configure your project to use JRebel, compile it and deploy it in eXo Platform as explained in step-1.

What is missing is an administration part to manage our application. An administrator must have the availability to configure the portlet. For instance, he may want to disable the comment feature.

To doing this, what is better that adding a portlet edit mode ?

Portlet Edit Mode

Juzu portlet is JSR286 compliant portlet. To provide edit mode, we need to tell portlet container that our portlet support to show edit mode. It’s why we need to modify our portlet.xml as below:

<portlet>
  <portlet-name>JuzcretApplication</portlet-name>
   ...
   <supports>
       <mime-type>text/html</mime-type>
       <portlet-mode>edit</portlet-mode>
     </supports>
...
</portlet>

Now JuZcret portlet have 2 modes: edit and view mode. We need to create a new template for the edit mode. in templates package add a new file editMode.gtmpl to display a checkbox to enable or not to comment secrets:

#{param name=enableComment/}
<form action="@{JuZcretApplication.enableComment()}" method="POST" role="form">
    <h5>Configuration</h5>
    <input type="checkbox" name="enableComment" <%=enableComment ? "checked" : "" %>/>Enable Comment
    <button type="submit">Save</button>
</form>

Our JuZcret application configuration will rely on the portlet preference mechanism.
Juzu framework provide a juzu-portlet plugin which help to bind portlet preference to our IOC container and allow use to inject and use PortletPreferences in our controller to store the configuration data of our portlet.
To use it we need to add juzu-plugins-portlet and portlet-api dependency in the pom.xml:

<dependency>
      <groupId>javax.portlet</groupId>
      <artifactId>portlet-api</artifactId>
      <version>2.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.juzu</groupId>
      <artifactId>juzu-plugins-portlet</artifactId>
      <version>1.0.0</version>
    </dependency>

Now we can inject in our JuZcretApplication controller PortletPreferences using @Inject annotation. We use it in a new action controller method named enableComment which manage the submit of the edit form:

[...]
import juzu.bridge.portlet.JuzuPortlet;
[...]
import javax.portlet.PortletMode;
import javax.portlet.PortletPreferences;
import javax.portlet.ReadOnlyException;
import javax.portlet.ValidatorException;
import java.io.IOException;
[...]

public class JuZcretApplication  implements RequestLifeCycle {

    @Inject
    PortletPreferences prefs;

    public static final String ENABLE_COMMENT = "enableComment";

    @Action
    public Response.View enableComment(String enableComment) throws ReadOnlyException, ValidatorException, IOException {
        if ("on".equals(enableComment)) {
            enableComment = "true";
        }
        prefs.setValue(ENABLE_COMMENT, enableComment);
        prefs.store();
        return JuZcretApplication_.index().with(JuzuPortlet.PORTLET_MODE, PortletMode.VIEW);
    }

After saving the portlet preference, notice that we redirect the portlet to the View mode by responding with a Juzu property, the JuzuPortlet.PORTLET_MODE property type with the value PortletMode.VIEW.

Now JuZcret can be configure to disabled the comment feature. It means that we have to adapt our secretWall.gtmpl template to display or not the form for submitting comment by using the enableComment parameter:

#{param name=enableComment/}

  […]

  <% if (enableComment) { %>
      <div class="secret-action">
      […]
      </div>
  <% } %>

  […]

From security perspective, hiding the social toolbar on the bottom is not enough to prevent user from commenting, but for the sake of simplicity, we decide that it’s acceptable for this tutorial. So when you disabled comment you cannot like or comment secret. Social features are deactivated.

Then the last step is to inject the new editMode.gtmpl template to the controller and modify the index View controller to adapt the display accordingly to the current Portlet mode:

  @Inject
  @Path("editMode.gtmpl")
  org.juzu.tutorial.templates.editMode editMode;

  @View
  public Response.Content index(RequestContext context) {
    boolean enableComment = Boolean.parseBoolean(prefs.getValue(ENABLE_COMMENT, "false"));

    if (PortletMode.EDIT.equals(context.getProperty(JuzuPortlet.PORTLET_MODE))) {
      return editMode.with().enableComment(enableComment).ok();
    } else {
      return secretWall.with().enableComment(enableComment)
.secretsList(secretService.getSecrets()).ok();
    }
   }

To know the current Portlet mode, we use the RequestContext object automatically injected by Juzu that allow us to check the property JuzuPortlet.PORTLET_MODE.

Recompile your application with

$ mvn clean install

Stop eXo Platform, copy/paste the new created war in webapp folder and restart eXo Platform.

Go to the JuZcret page, click on Edit → Page → Edit Layout. Then mouse over the "Juzu Secret Application" and click on the "Edit Portlet" Icon.

Here you can disabled comments:

Portlet edit mode

After uncheck "enable comment", save and close the edit mode, you cannot add new comment via the secret wall:

Cannot add comment

We are at the end of the step 4 with a nice social application. But our JuZcret application miss an important thing from production. We don’t persist data, all is saved in memory… For fix it, go ahead to the step 5!