org.entcore.conversation.controllers.ConversationController.java Source code

Java tutorial

Introduction

Here is the source code for org.entcore.conversation.controllers.ConversationController.java

Source

/* Copyright  "Open Digital Education", 2014
 *
 * This program is published by "Open Digital Education".
 * You must indicate the name of the software and the company in any production /contribution
 * using the software and indicate on the home page of the software industry in question,
 * "powered by Open Digital Education" with a reference to the website: https://opendigitaleducation.com/.
 *
 * This program is free software, licensed under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation, version 3 of the License.
 *
 * You can redistribute this application and/or modify it since you respect the terms of the GNU Affero General Public License.
 * If you modify the source code and then use this modified source code in your creation, you must make available the source code of your modifications.
 *
 * You should have received a copy of the GNU Affero General Public License along with the software.
 * If not, please see : <http://www.gnu.org/licenses/>. Full compliance requires reading the terms of this license and following its directives.
    
 *
 */

package org.entcore.conversation.controllers;

import fr.wseduc.bus.BusAddress;
import fr.wseduc.rs.Delete;
import fr.wseduc.rs.Get;
import fr.wseduc.rs.Post;
import fr.wseduc.rs.Put;
import fr.wseduc.webutils.I18n;
import fr.wseduc.webutils.http.BaseController;
import fr.wseduc.webutils.request.RequestUtils;

import io.vertx.core.AsyncResult;
import io.vertx.core.file.FileSystem;
import io.vertx.core.http.HttpServerResponse;
import org.entcore.common.events.EventStore;
import org.entcore.common.events.EventStoreFactory;
import org.entcore.common.http.filter.ResourceFilter;
import org.entcore.common.http.request.JsonHttpServerRequest;
import org.entcore.common.notification.TimelineHelper;
import org.entcore.common.storage.Storage;
import org.entcore.common.user.UserInfos;
import org.entcore.common.user.UserUtils;
import org.entcore.common.utils.Config;
import org.entcore.common.utils.StringUtils;
import org.entcore.common.utils.Zip;
import org.entcore.conversation.Conversation;
import org.entcore.conversation.filters.MessageOwnerFilter;
import org.entcore.conversation.filters.MessageUserFilter;
import org.entcore.conversation.filters.MultipleMessageUserFilter;
import org.entcore.conversation.filters.VisiblesFilter;
import org.entcore.conversation.filters.FoldersFilter;
import org.entcore.conversation.filters.FoldersMessagesFilter;
import org.entcore.conversation.service.ConversationService;
import org.entcore.conversation.service.impl.Neo4jConversationService;
import org.entcore.conversation.service.impl.SqlConversationService;

import fr.wseduc.webutils.Either;
import fr.wseduc.webutils.Utils;
import fr.wseduc.security.ActionType;
import fr.wseduc.security.SecuredAction;

import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.Message;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.vertx.java.core.http.RouteMatcher;

import java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.Deflater;

import static fr.wseduc.webutils.Utils.getOrElse;
import static fr.wseduc.webutils.Utils.handlerToAsyncHandler;
import static fr.wseduc.webutils.request.RequestUtils.bodyToJson;
import static org.entcore.common.http.response.DefaultResponseHandler.*;
import static org.entcore.common.user.UserUtils.getUserInfos;

public class ConversationController extends BaseController {

    private final static String QUOTA_BUS_ADDRESS = "org.entcore.workspace.quota";

    private Storage storage;
    private int threshold;

    private ConversationService conversationService;
    private Neo4jConversationService neoConversationService;
    private TimelineHelper notification;
    private EventStore eventStore;

    private enum ConversationEvent {
        GET_RESOURCE, ACCESS
    }

    private final String exportPath;

    public ConversationController(Storage storage, String exportPath) {
        this.storage = storage;
        this.exportPath = exportPath;
    }

    @Override
    public void init(Vertx vertx, JsonObject config, RouteMatcher rm,
            Map<String, fr.wseduc.webutils.security.SecuredAction> securedActions) {
        super.init(vertx, config, rm, securedActions);
        /*
        this.conversationService = new DefaultConversationService(vertx,
        config.getString("app-name", Conversation.class.getSimpleName()));
        */
        this.conversationService = new SqlConversationService(vertx, config.getString("db-schema", "conversation"));
        this.neoConversationService = new Neo4jConversationService();
        notification = new TimelineHelper(vertx, eb, config);
        eventStore = EventStoreFactory.getFactory().getEventStore(Conversation.class.getSimpleName());
        this.threshold = config.getInteger("alertStorage", 80);
    }

    @Get("conversation")
    @SecuredAction("conversation.view")
    public void view(HttpServerRequest request) {
        renderView(request);
        eventStore.createAndStoreEvent(ConversationEvent.ACCESS.name(), request);
    }

    @Post("draft")
    @SecuredAction("conversation.create.draft")
    public void createDraft(final HttpServerRequest request) {
        getUserInfos(eb, request, new Handler<UserInfos>() {
            @Override
            public void handle(final UserInfos user) {
                if (user != null) {
                    final String parentMessageId = request.params().get("In-Reply-To");
                    bodyToJson(request, new Handler<JsonObject>() {
                        @Override
                        public void handle(final JsonObject message) {

                            if (!message.containsKey("from")) {
                                message.put("from", user.getUserId());
                            }

                            final Handler<JsonObject> parentHandler = new Handler<JsonObject>() {
                                @Override
                                public void handle(JsonObject parent) {
                                    final String threadId;
                                    if (parent != null) {
                                        threadId = parent.getString("thread_id");
                                    } else {
                                        threadId = null;
                                    }
                                    neoConversationService.addDisplayNames(message, parent,
                                            new Handler<JsonObject>() {
                                                public void handle(JsonObject message) {
                                                    conversationService.saveDraft(parentMessageId, threadId,
                                                            message, user, defaultResponseHandler(request, 201));
                                                }
                                            });
                                }
                            };

                            if (parentMessageId != null && !parentMessageId.trim().isEmpty()) {
                                conversationService.get(parentMessageId, user,
                                        new Handler<Either<String, JsonObject>>() {
                                            public void handle(Either<String, JsonObject> event) {
                                                if (event.isLeft()) {
                                                    badRequest(request);
                                                    return;
                                                }

                                                parentHandler.handle(event.right().getValue());
                                            }
                                        });
                            } else {
                                parentHandler.handle(null);
                            }

                        }
                    });
                } else {
                    unauthorized(request);
                }
            }
        });
    }

    @Put("draft/:id")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(MessageOwnerFilter.class)
    public void updateDraft(final HttpServerRequest request) {
        final String messageId = request.params().get("id");

        if (messageId == null || messageId.trim().isEmpty()) {
            badRequest(request);
            return;
        }

        getUserInfos(eb, request, new Handler<UserInfos>() {
            @Override
            public void handle(final UserInfos user) {
                if (user != null) {
                    bodyToJson(request, new Handler<JsonObject>() {
                        @Override
                        public void handle(JsonObject message) {
                            if (!message.containsKey("from")) {
                                message.put("from", user.getUserId());
                            }
                            neoConversationService.addDisplayNames(message, null, new Handler<JsonObject>() {
                                public void handle(JsonObject message) {
                                    conversationService.updateDraft(messageId, message, user,
                                            defaultResponseHandler(request));
                                }
                            });
                        }
                    });
                } else {
                    unauthorized(request);
                }
            }
        });
    }

    private void saveAndSend(final String messageId, final JsonObject message, final UserInfos user,
            final String parentMessageId, final String threadId, final Handler<Either<String, JsonObject>> result) {

        Handler<Either<String, JsonObject>> handler = new Handler<Either<String, JsonObject>>() {
            @Override
            public void handle(Either<String, JsonObject> event) {
                if (event.isLeft()) {
                    result.handle(event);
                    return;
                }

                final String id = (messageId != null && !messageId.trim().isEmpty()) ? messageId
                        : event.right().getValue().getString("id");

                conversationService.get(id, user, new Handler<Either<String, JsonObject>>() {
                    public void handle(Either<String, JsonObject> event) {
                        if (event.isLeft()) {
                            result.handle(event);
                            return;
                        }

                        JsonObject msg = event.right().getValue();
                        JsonArray attachments = msg.getJsonArray("attachments",
                                new fr.wseduc.webutils.collections.JsonArray());
                        final AtomicLong size = new AtomicLong(0l);

                        for (Object att : attachments) {
                            size.addAndGet(((JsonObject) att).getLong("size", 0l));
                        }

                        neoConversationService.findInactives(message, size.get(), new Handler<JsonObject>() {
                            public void handle(JsonObject userDetails) {
                                message.mergeIn(userDetails);

                                conversationService.send(parentMessageId, id, message, user,
                                        new Handler<Either<String, JsonObject>>() {
                                            public void handle(Either<String, JsonObject> event) {
                                                if (event.isRight()) {
                                                    for (Object recipient : message.getJsonArray("allUsers",
                                                            new fr.wseduc.webutils.collections.JsonArray())) {
                                                        if (recipient.toString().equals(user.getUserId()))
                                                            continue;
                                                        updateUserQuota(recipient.toString(), size.get());
                                                    }
                                                }
                                                result.handle(event);
                                            }
                                        });
                            }
                        });
                    }
                });
            }
        };
        if (messageId != null && !messageId.trim().isEmpty()) {
            conversationService.updateDraft(messageId, message, user, handler);
        } else {
            conversationService.saveDraft(parentMessageId, threadId, message, user, handler);
        }
    }

    @Post("send")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(VisiblesFilter.class)
    public void send(final HttpServerRequest request) {
        final String messageId = request.params().get("id");

        getUserInfos(eb, request, new Handler<UserInfos>() {
            @Override
            public void handle(final UserInfos user) {
                if (user != null) {
                    final String parentMessageId = request.params().get("In-Reply-To");
                    bodyToJson(request, new Handler<JsonObject>() {
                        @Override
                        public void handle(final JsonObject message) {
                            if (!message.containsKey("from")) {
                                message.put("from", user.getUserId());
                            }

                            final Handler<JsonObject> parentHandler = new Handler<JsonObject>() {
                                public void handle(JsonObject parentMsg) {
                                    final String threadId;
                                    if (parentMsg != null) {
                                        threadId = parentMsg.getString("thread_id");
                                    } else {
                                        threadId = null;
                                    }
                                    neoConversationService.addDisplayNames(message, parentMsg,
                                            new Handler<JsonObject>() {
                                                public void handle(final JsonObject message) {
                                                    saveAndSend(messageId, message, user, parentMessageId, threadId,
                                                            new Handler<Either<String, JsonObject>>() {
                                                                @Override
                                                                public void handle(
                                                                        Either<String, JsonObject> event) {
                                                                    if (event.isRight()) {
                                                                        JsonObject result = event.right()
                                                                                .getValue();
                                                                        JsonObject timelineParams = new JsonObject()
                                                                                .put("subject",
                                                                                        result.getString("subject"))
                                                                                .put("body",
                                                                                        StringUtils.stripHtmlTag(
                                                                                                result.getString(
                                                                                                        "body")))
                                                                                .put("id", result.getString("id"))
                                                                                .put("sentIds",
                                                                                        message.getJsonArray(
                                                                                                "allUsers",
                                                                                                new fr.wseduc.webutils.collections.JsonArray()));
                                                                        timelineNotification(request,
                                                                                timelineParams, user);
                                                                        renderJson(request, result.put("inactive",
                                                                                message.getJsonArray("inactives",
                                                                                        new fr.wseduc.webutils.collections.JsonArray()))
                                                                                .put("undelivered",
                                                                                        message.getJsonArray(
                                                                                                "undelivered",
                                                                                                new fr.wseduc.webutils.collections.JsonArray()))
                                                                                .put("sent", message.getJsonArray(
                                                                                        "allUsers",
                                                                                        new fr.wseduc.webutils.collections.JsonArray())
                                                                                        .size()));
                                                                    } else {
                                                                        JsonObject error = new JsonObject().put(
                                                                                "error", event.left().getValue());
                                                                        renderJson(request, error, 400);
                                                                    }
                                                                }
                                                            });
                                                }
                                            });
                                }
                            };

                            if (parentMessageId != null && !parentMessageId.trim().isEmpty()) {
                                conversationService.get(parentMessageId, user,
                                        new Handler<Either<String, JsonObject>>() {
                                            public void handle(Either<String, JsonObject> event) {
                                                if (event.isLeft()) {
                                                    badRequest(request);
                                                    return;
                                                }

                                                parentHandler.handle(event.right().getValue());
                                            }
                                        });
                            } else {
                                parentHandler.handle(null);
                            }
                        }
                    });
                } else {
                    unauthorized(request);
                }
            }
        });
    }

    private void timelineNotification(HttpServerRequest request, JsonObject sentMessage, UserInfos user) {
        log.debug(sentMessage.encode());
        JsonArray r = sentMessage.getJsonArray("sentIds");
        String id = sentMessage.getString("id");
        String subject = sentMessage.getString("subject", "<span translate key=\"timeline.no.subject\"></span>");
        sentMessage.remove("sentIds");
        sentMessage.remove("id");
        sentMessage.remove("subject");
        if (r == null || id == null || user == null) {
            return;
        }
        final JsonObject params = new JsonObject()
                .put("uri", "/userbook/annuaire#" + user.getUserId() + "#" + user.getType())
                .put("username", user.getUsername()).put("subject", subject)
                .put("messageUri", pathPrefix + "/conversation#/read-mail/" + id);
        params.put("resourceUri", params.getString("messageUri"));
        params.put("pushNotif", new JsonObject().put("title", "push.notif.new.message").put("body",
                user.getUsername() + " : " + sentMessage.getString("body")));
        List<String> recipients = new ArrayList<>();
        String idTmp;
        for (Object o : r) {
            if (!(o instanceof String))
                continue;
            idTmp = (String) o;
            if (!user.getUserId().equals(idTmp))
                recipients.add(idTmp);
        }
        notification.notifyTimeline(request, "messagerie.send-message", user, recipients, id, params);
    }

    @Get("list/:folder")
    @SecuredAction(value = "conversation.list", type = ActionType.AUTHENTICATED)
    public void list(final HttpServerRequest request) {
        final String folder = request.params().get("folder");
        final String restrain = request.params().get("restrain");
        final String unread = request.params().get("unread");
        final String search = request.params().get("search");
        if (search != null && search.trim().length() < 3) {
            badRequest(request);
            return;
        }
        final String p = Utils.getOrElse(request.params().get("page"), "0", false);
        if (folder == null || folder.trim().isEmpty()) {
            badRequest(request);
            return;
        }
        getUserInfos(eb, request, new Handler<UserInfos>() {
            @Override
            public void handle(final UserInfos user) {
                if (user != null) {
                    int page;
                    try {
                        page = Integer.parseInt(p);
                    } catch (NumberFormatException e) {
                        page = 0;
                    }
                    Boolean b = null;
                    ;
                    if (unread != null && !unread.isEmpty()) {
                        b = Boolean.valueOf(unread);
                    }
                    conversationService.list(folder, restrain, b, user, page, search,
                            new Handler<Either<String, JsonArray>>() {
                                @Override
                                public void handle(Either<String, JsonArray> r) {
                                    if (r.isRight()) {
                                        for (Object o : r.right().getValue()) {
                                            if (!(o instanceof JsonObject)) {
                                                continue;
                                            }
                                            translateGroupsNames((JsonObject) o, request);
                                        }
                                        renderJson(request, r.right().getValue());
                                    } else {
                                        JsonObject error = new JsonObject().put("error", r.left().getValue());
                                        renderJson(request, error, 400);
                                    }
                                }
                            });
                } else {
                    unauthorized(request);
                }
            }
        });
    }

    private void translateGroupsNames(JsonObject message, HttpServerRequest request) {
        JsonArray d3 = new fr.wseduc.webutils.collections.JsonArray();
        for (Object o2 : getOrElse(message.getJsonArray("displayNames"),
                new fr.wseduc.webutils.collections.JsonArray())) {
            if (!(o2 instanceof String)) {
                continue;
            }
            String[] a = ((String) o2).split("\\$");
            if (a.length != 4) {
                continue;
            }
            JsonArray d2 = new fr.wseduc.webutils.collections.JsonArray().add(a[0]);
            if (a[2] != null && !a[2].trim().isEmpty()) {
                final String groupDisplayName = (a[3] != null && !a[3].trim().isEmpty()) ? a[3] : null;
                d2.add(UserUtils.groupDisplayName(a[2], groupDisplayName, I18n.acceptLanguage(request)));
            } else {
                d2.add(a[1]);
            }
            d3.add(d2);
        }
        message.put("displayNames", d3);
        JsonArray toName = message.getJsonArray("toName");
        if (toName != null) {
            JsonArray d2 = new fr.wseduc.webutils.collections.JsonArray();
            message.put("toName", d2);
            for (Object o : toName) {
                if (!(o instanceof String)) {
                    continue;
                }
                d2.add(UserUtils.groupDisplayName((String) o, null, I18n.acceptLanguage(request)));
            }
        }
        JsonArray ccName = message.getJsonArray("ccName");
        if (ccName != null) {
            JsonArray d2 = new fr.wseduc.webutils.collections.JsonArray();
            message.put("ccName", d2);
            for (Object o : ccName) {
                if (!(o instanceof String)) {
                    continue;
                }
                d2.add(UserUtils.groupDisplayName((String) o, null, I18n.acceptLanguage(request)));
            }
        }
    }

    @Get("threads/list")
    @SecuredAction(value = "conversation.threads.list")
    public void listThreads(final HttpServerRequest request) {
        final String p = Utils.getOrElse(request.params().get("page"), "0", false);
        getUserInfos(eb, request, new Handler<UserInfos>() {
            @Override
            public void handle(final UserInfos user) {
                if (user != null) {
                    int page;
                    try {
                        page = Integer.parseInt(p);
                    } catch (NumberFormatException e) {
                        page = 0;
                    }
                    conversationService.listThreads(user, page, new Handler<Either<String, JsonArray>>() {
                        @Override
                        public void handle(Either<String, JsonArray> r) {
                            if (r.isRight()) {
                                HashMap<String, JsonArray> test = new HashMap<String, JsonArray>();
                                JsonObject tmp;
                                String threadId;
                                JsonArray result = new fr.wseduc.webutils.collections.JsonArray();
                                for (Object o : r.right().getValue()) {
                                    if (!(o instanceof JsonObject)) {
                                        continue;
                                    }
                                    tmp = (JsonObject) o;
                                    translateGroupsNames(tmp, request);
                                    threadId = tmp.getString("thread_id");
                                    if (threadId != null) {
                                        if (test.containsKey(threadId))
                                            test.get(threadId).add(tmp);
                                        else
                                            test.put(threadId,
                                                    new fr.wseduc.webutils.collections.JsonArray().add(tmp));
                                    } else {
                                        result.add(new fr.wseduc.webutils.collections.JsonArray().add(tmp));
                                    }
                                }

                                for (JsonArray array : test.values()) {
                                    result.add(array);
                                }
                                renderJson(request, result);
                            } else {
                                JsonObject error = new JsonObject().put("error", r.left().getValue());
                                renderJson(request, error, 400);
                            }
                        }
                    });
                } else {
                    unauthorized(request);
                }
            }
        });
    }

    @Get("thread/messages/:id")
    @SecuredAction(value = "conversation.threads.message", type = ActionType.AUTHENTICATED)
    public void listThreadMessages(final HttpServerRequest request) {
        final String threadId = request.params().get("id");
        final String p = Utils.getOrElse(request.params().get("page"), "0", false);

        if (threadId == null || threadId.trim().isEmpty()) {
            badRequest(request);
            return;
        }
        getUserInfos(eb, request, new Handler<UserInfos>() {
            @Override
            public void handle(final UserInfos user) {
                if (user != null) {
                    int page;
                    try {
                        page = Integer.parseInt(p);
                    } catch (NumberFormatException e) {
                        page = 0;
                    }
                    conversationService.listThreadMessages(threadId, page, user,
                            new Handler<Either<String, JsonArray>>() {
                                @Override
                                public void handle(Either<String, JsonArray> r) {
                                    if (r.isRight()) {
                                        for (Object o : r.right().getValue()) {
                                            if (!(o instanceof JsonObject)) {
                                                continue;
                                            }
                                            translateGroupsNames((JsonObject) o, request);
                                        }
                                        renderJson(request, r.right().getValue());
                                    } else {
                                        JsonObject error = new JsonObject().put("error", r.left().getValue());
                                        renderJson(request, error, 400);
                                    }
                                }
                            });
                } else {
                    unauthorized(request);
                }
            }
        });
    }

    @Get("thread/previous-messages/:id")
    @SecuredAction(value = "conversation.threads.previous")
    @ResourceFilter(MessageUserFilter.class)
    public void listPreviousMessages(final HttpServerRequest request) {
        final String parentId = request.params().get("id");
        if (parentId == null || parentId.trim().isEmpty()) {
            badRequest(request);
            return;
        }
        listMessages(request, parentId, true);
    }

    @Get("thread/new-messages/:id")
    @SecuredAction(value = "conversation.threads.new")
    @ResourceFilter(MessageUserFilter.class)
    public void listNewMessages(final HttpServerRequest request) {
        final String messageId = request.params().get("id");
        if (messageId == null || messageId.trim().isEmpty()) {
            badRequest(request);
            return;
        }
        listMessages(request, messageId, false);
    }

    private void listMessages(final HttpServerRequest request, final String messageId, final boolean listPrevious) {
        getUserInfos(eb, request, new Handler<UserInfos>() {
            @Override
            public void handle(final UserInfos user) {
                if (user != null) {
                    conversationService.listThreadMessagesNavigation(messageId, listPrevious, user,
                            new Handler<Either<String, JsonArray>>() {
                                @Override
                                public void handle(Either<String, JsonArray> r) {
                                    if (r.isRight()) {
                                        for (Object o : r.right().getValue()) {
                                            if (!(o instanceof JsonObject)) {
                                                continue;
                                            }
                                            translateGroupsNames((JsonObject) o, request);
                                        }
                                        renderJson(request, r.right().getValue());
                                    } else {
                                        JsonObject error = new JsonObject().put("error", r.left().getValue());
                                        renderJson(request, error, 400);
                                    }
                                }
                            });
                } else {
                    unauthorized(request);
                }
            }
        });
    }

    @Get("count/:folder")
    @SecuredAction(value = "conversation.count", type = ActionType.AUTHENTICATED)
    public void count(final HttpServerRequest request) {
        final String folder = request.params().get("folder");
        final String restrain = request.params().get("restrain");
        final String unread = request.params().get("unread");
        if (folder == null || folder.trim().isEmpty()) {
            badRequest(request);
            return;
        }
        getUserInfos(eb, request, new Handler<UserInfos>() {
            @Override
            public void handle(final UserInfos user) {
                if (user != null) {
                    Boolean b = null;
                    if (unread != null && !unread.isEmpty()) {
                        b = Boolean.valueOf(unread);
                    }
                    conversationService.count(folder, restrain, b, user, defaultResponseHandler(request));
                } else {
                    unauthorized(request);
                }
            }
        });
    }

    @Get("visible")
    @SecuredAction(value = "conversation.visible", type = ActionType.AUTHENTICATED)
    public void visible(final HttpServerRequest request) {
        getUserInfos(eb, request, new Handler<UserInfos>() {
            @Override
            public void handle(final UserInfos user) {
                if (user != null) {
                    String parentMessageId = request.params().get("In-Reply-To");
                    conversationService.findVisibleRecipients(parentMessageId, user, I18n.acceptLanguage(request),
                            request.params().get("search"), defaultResponseHandler(request));
                } else {
                    unauthorized(request);
                }
            }
        });
    }

    @Get("message/:id")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(MessageUserFilter.class)
    public void getMessage(final HttpServerRequest request) {
        final String id = request.params().get("id");
        if (id == null || id.trim().isEmpty()) {
            badRequest(request);
            return;
        }
        getUserInfos(eb, request, new Handler<UserInfos>() {
            @Override
            public void handle(final UserInfos user) {
                if (user != null) {
                    conversationService.get(id, user, new Handler<Either<String, JsonObject>>() {
                        @Override
                        public void handle(Either<String, JsonObject> r) {
                            if (r.isRight()) {
                                translateGroupsNames(r.right().getValue(), request);
                                renderJson(request, r.right().getValue());
                                eventStore.createAndStoreEvent(ConversationEvent.GET_RESOURCE.name(), request,
                                        new JsonObject().put("resource", id));
                            } else {
                                JsonObject error = new JsonObject().put("error", r.left().getValue());
                                renderJson(request, error, 400);
                            }
                        }
                    });
                } else {
                    unauthorized(request);
                }
            }
        });
    }

    @Put("trash")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(MultipleMessageUserFilter.class)
    public void trash(final HttpServerRequest request) {

        bodyToJson(request, new Handler<JsonObject>() {
            @Override
            public void handle(JsonObject body) {
                JsonArray ids = body.getJsonArray("id");
                if (ids == null || ids.isEmpty()) {
                    badRequest(request);
                    return;
                }

                getUserInfos(eb, request, new Handler<UserInfos>() {
                    @Override
                    public void handle(final UserInfos user) {
                        if (user != null) {
                            conversationService.trash(ids.getList(), user, defaultResponseHandler(request));
                        } else {
                            unauthorized(request);
                        }
                    }
                });
            }
        });
    }

    @Put("restore")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(MultipleMessageUserFilter.class)
    public void restore(final HttpServerRequest request) {

        bodyToJson(request, new Handler<JsonObject>() {
            @Override
            public void handle(JsonObject body) {
                JsonArray ids = body.getJsonArray("id");
                if (ids == null || ids.isEmpty()) {
                    badRequest(request);
                    return;
                }

                getUserInfos(eb, request, new Handler<UserInfos>() {
                    @Override
                    public void handle(final UserInfos user) {
                        if (user != null) {
                            conversationService.restore(ids.getList(), user, defaultResponseHandler(request));
                        } else {
                            unauthorized(request);
                        }
                    }
                });
            }
        });
    }

    @Put("delete")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(MultipleMessageUserFilter.class)
    public void delete(final HttpServerRequest request) {
        bodyToJson(request, new Handler<JsonObject>() {
            @Override
            public void handle(JsonObject body) {
                JsonArray ids = body.getJsonArray("id");
                if (ids == null || ids.isEmpty()) {
                    badRequest(request);
                    return;
                }
                deleteMessages(request, ids.getList(), false);
            }
        });
    }

    private void deleteMessages(final HttpServerRequest request, final List<String> ids, final Boolean deleteAll) {
        deleteMessages(request, ids, deleteAll, null);
    }

    private void deleteMessages(final HttpServerRequest request, final List<String> ids, final Boolean deleteAll,
            Handler<Either<String, JsonArray>> handler) {
        getUserInfos(eb, request, new Handler<UserInfos>() {
            @Override
            public void handle(final UserInfos user) {
                if (user != null) {
                    conversationService.delete(ids, deleteAll, user, new Handler<Either<String, JsonArray>>() {
                        @Override
                        public void handle(Either<String, JsonArray> event) {
                            if (event.isLeft()) {
                                if (handler != null) {
                                    handler.handle(event);
                                } else {
                                    badRequest(request, event.left().getValue());
                                }
                                return;
                            }

                            JsonArray results = event.right().getValue();
                            final long freeQuota = results.getJsonArray(0).getJsonObject(0).getLong("totalquota",
                                    0L);

                            updateUserQuota(user.getUserId(), -freeQuota, new Handler<Void>() {
                                public void handle(Void event) {
                                    if (handler != null) {
                                        handler.handle(new Either.Right<>(new JsonArray()));
                                    } else {
                                        ok(request);
                                    }
                                }
                            });

                        }
                    });
                } else {
                    unauthorized(request);
                }
            }
        });
    }

    @Delete("emptyTrash")
    @SecuredAction(value = "conversation.empty.trash", type = ActionType.AUTHENTICATED)
    public void emptyTrash(final HttpServerRequest request) {
        AtomicInteger count = new AtomicInteger(2);
        AtomicBoolean error = new AtomicBoolean(false);
        Handler<Either<String, JsonArray>> handler = event -> {
            if (event.isLeft()) {
                error.set(true);
            }
            if (count.decrementAndGet() == 0) {
                if (error.get()) {
                    badRequest(request);
                } else {
                    ok(request);
                }
            }
        };
        deleteMessages(request, null, true, handler);
        deleteFolders(request, null, true, handler);
    }

    //Mark messages as unread / read
    @Post("toggleUnread")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(MultipleMessageUserFilter.class)
    public void toggleUnread(final HttpServerRequest request) {
        bodyToJson(request, new Handler<JsonObject>() {
            @Override
            public void handle(JsonObject body) {
                final JsonArray ids = body.getJsonArray("id");
                final Boolean unread = body.getBoolean("unread");
                if (ids == null || ids.isEmpty() || unread == null) {
                    badRequest(request);
                    return;
                }
                UserUtils.getUserInfos(eb, request, new Handler<UserInfos>() {
                    @Override
                    public void handle(final UserInfos user) {
                        if (user != null) {
                            conversationService.toggleUnread(ids.getList(), unread, user,
                                    defaultResponseHandler(request));
                        } else {
                            unauthorized(request);
                        }
                    }
                });
            }
        });
    }

    //Get max folder depth
    @Get("max-depth")
    @SecuredAction(value = "conversation.max.depth", type = ActionType.AUTHENTICATED)
    public void getMaxDepth(final HttpServerRequest request) {
        renderJson(request, new JsonObject().put("max-depth",
                Config.getConf().getInteger("max-folder-depth", Conversation.DEFAULT_FOLDER_DEPTH)));
    }

    //List folders at a given depth, or trashed folders at depth 1 only.
    @Get("folders/list")
    @SecuredAction(value = "conversation.folder.list", type = ActionType.AUTHENTICATED)
    public void listFolders(final HttpServerRequest request) {
        final String parentId = request.params().get("parentId");
        final String listTrash = request.params().get("trash");

        Handler<UserInfos> userInfosHandler = new Handler<UserInfos>() {
            public void handle(final UserInfos user) {
                if (user == null) {
                    unauthorized(request);
                    return;
                }
                if (listTrash != null) {
                    conversationService.listTrashedFolders(user, arrayResponseHandler(request));
                } else {
                    conversationService.listFolders(parentId, user, arrayResponseHandler(request));
                }
            }
        };

        UserUtils.getUserInfos(eb, request, userInfosHandler);
    }

    //Create a new folder at root level or inside a user folder.
    @Post("folder")
    @SecuredAction(value = "conversation.folder.create", type = ActionType.AUTHENTICATED)
    public void createFolder(final HttpServerRequest request) {

        Handler<UserInfos> userInfosHandler = new Handler<UserInfos>() {
            public void handle(final UserInfos user) {
                if (user == null) {
                    unauthorized(request);
                    return;
                }
                RequestUtils.bodyToJson(request, pathPrefix + "createFolder", new Handler<JsonObject>() {
                    public void handle(JsonObject body) {
                        final String name = body.getString("name");
                        final String parentId = body.getString("parentId", null);

                        if (name == null || name.trim().length() == 0) {
                            badRequest(request);
                            return;
                        }
                        conversationService.createFolder(name, parentId, user,
                                defaultResponseHandler(request, 201));
                    }
                });
            }
        };

        UserUtils.getUserInfos(eb, request, userInfosHandler);
    }

    //Update a folder
    @Put("folder/:folderId")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(FoldersFilter.class)
    public void updateFolder(final HttpServerRequest request) {
        final String folderId = request.params().get("folderId");

        Handler<UserInfos> userInfosHandler = new Handler<UserInfos>() {
            public void handle(final UserInfos user) {
                RequestUtils.bodyToJson(request, pathPrefix + "updateFolder", new Handler<JsonObject>() {
                    public void handle(JsonObject data) {
                        conversationService.updateFolder(folderId, data, user,
                                defaultResponseHandler(request, 200));
                    }
                });
            }
        };

        UserUtils.getUserInfos(eb, request, userInfosHandler);
    }

    //Move messages into a folder
    @Put("move/userfolder/:folderId")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(FoldersMessagesFilter.class)
    public void move(final HttpServerRequest request) {
        final String folderId = request.params().get("folderId");
        bodyToJson(request, new Handler<JsonObject>() {
            @Override
            public void handle(JsonObject body) {
                final JsonArray messageIds = body.getJsonArray("id");
                if (messageIds == null || messageIds.size() == 0) {
                    badRequest(request);
                    return;
                }

                Handler<UserInfos> userInfosHandler = new Handler<UserInfos>() {
                    public void handle(final UserInfos user) {
                        if (user == null) {
                            unauthorized(request);
                            return;
                        }
                        conversationService.moveToFolder(messageIds.getList(), folderId, user,
                                defaultResponseHandler(request));
                    }
                };

                UserUtils.getUserInfos(eb, request, userInfosHandler);

            }
        });
    }

    //Move messages into a system folder
    @Put("move/root")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(MessageUserFilter.class)
    public void rootMove(final HttpServerRequest request) {
        final List<String> messageIds = request.params().getAll("id");

        if (messageIds == null || messageIds.size() == 0) {
            badRequest(request);
            return;
        }

        Handler<UserInfos> userInfosHandler = new Handler<UserInfos>() {
            public void handle(final UserInfos user) {
                if (user == null) {
                    unauthorized(request);
                    return;
                }
                conversationService.backToSystemFolder(messageIds, user, defaultResponseHandler(request));
            }
        };

        UserUtils.getUserInfos(eb, request, userInfosHandler);
    }

    //Trash a folder
    @Put("folder/trash/:folderId")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(FoldersFilter.class)
    public void trashFolder(final HttpServerRequest request) {
        final String folderId = request.params().get("folderId");

        Handler<UserInfos> userInfosHandler = new Handler<UserInfos>() {
            public void handle(final UserInfos user) {
                if (user == null) {
                    unauthorized(request);
                    return;
                }
                conversationService.trashFolder(folderId, user, defaultResponseHandler(request));
            }
        };

        UserUtils.getUserInfos(eb, request, userInfosHandler);
    }

    //Restore a trashed folder
    @Put("folder/restore/:folderId")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(FoldersFilter.class)
    public void restoreFolder(final HttpServerRequest request) {
        final String folderId = request.params().get("folderId");

        Handler<UserInfos> userInfosHandler = new Handler<UserInfos>() {
            public void handle(final UserInfos user) {
                if (user == null) {
                    unauthorized(request);
                    return;
                }
                conversationService.restoreFolder(folderId, user, defaultResponseHandler(request));
            }
        };

        UserUtils.getUserInfos(eb, request, userInfosHandler);
    }

    //Delete a trashed folder
    @Delete("folder/:folderId")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(FoldersFilter.class)
    public void deleteFolder(final HttpServerRequest request) {
        final String folderId = request.params().get("folderId");

        deleteFolders(request, folderId, false);
    }

    private void deleteFolders(final HttpServerRequest request, final String folderId, final Boolean deleteAll) {
        deleteFolders(request, folderId, deleteAll, null);
    }

    private void deleteFolders(final HttpServerRequest request, final String folderId, final Boolean deleteAll,
            final Handler<Either<String, JsonArray>> handler) {
        Handler<UserInfos> userInfosHandler = new Handler<UserInfos>() {
            public void handle(final UserInfos user) {
                if (user == null) {
                    unauthorized(request);
                    return;
                }
                conversationService.deleteFolder(folderId, deleteAll, user,
                        new Handler<Either<String, JsonArray>>() {
                            public void handle(Either<String, JsonArray> event) {
                                if (event.isLeft()) {
                                    if (handler != null) {
                                        handler.handle(event);
                                    } else {
                                        badRequest(request, event.left().getValue());
                                    }
                                    return;
                                }

                                JsonArray results = event.right().getValue();
                                final long freeQuota = results.getJsonArray(0).getJsonObject(0)
                                        .getLong("totalquota", 0L);

                                updateUserQuota(user.getUserId(), -freeQuota, new Handler<Void>() {
                                    public void handle(Void event) {
                                        if (handler != null) {
                                            handler.handle(new Either.Right<>(new JsonArray()));
                                        } else {
                                            ok(request);
                                        }
                                    }
                                });

                            }
                        });
            }
        };

        UserUtils.getUserInfos(eb, request, userInfosHandler);
    }

    //Post an new attachment to a drafted message
    @Post("message/:id/attachment")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(MessageUserFilter.class)
    public void postAttachment(final HttpServerRequest request) {
        final String messageId = request.params().get("id");

        Handler<UserInfos> userInfosHandler = new Handler<UserInfos>() {
            public void handle(final UserInfos user) {
                if (user == null) {
                    unauthorized(request);
                    return;
                }
                request.pause();
                getUserQuota(user.getUserId(), new Handler<JsonObject>() {
                    public void handle(JsonObject j) {

                        request.resume();

                        if (j == null || "error".equals(j.getString("status"))) {
                            badRequest(request, j == null ? "" : j.getString("message"));
                            return;
                        }

                        long quota = j.getLong("quota", 0l);
                        long storage = j.getLong("storage", 0l);

                        ConversationController.this.storage.writeUploadFile(request, (quota - storage),
                                new Handler<JsonObject>() {
                                    public void handle(final JsonObject uploaded) {
                                        if (!"ok".equals(uploaded.getString("status"))) {
                                            badRequest(request, uploaded.getString("message"));
                                            return;
                                        }

                                        updateUserQuota(user.getUserId(), uploaded
                                                .getJsonObject("metadata", new JsonObject()).getLong("size", 0L),
                                                new Handler<Void>() {
                                                    @Override
                                                    public void handle(Void v) {
                                                        conversationService.addAttachment(messageId, user, uploaded,
                                                                defaultResponseHandler(request));
                                                    }
                                                });
                                    }
                                });
                    }
                });
            }
        };

        UserUtils.getUserInfos(eb, request, userInfosHandler);
    }

    //Download an attachment
    @Get("message/:id/attachment/:attachmentId")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(MessageUserFilter.class)
    public void getAttachment(final HttpServerRequest request) {
        final String messageId = request.params().get("id");
        final String attachmentId = request.params().get("attachmentId");

        Handler<UserInfos> userInfosHandler = new Handler<UserInfos>() {
            public void handle(final UserInfos user) {
                if (user == null) {
                    unauthorized(request);
                    return;
                }

                conversationService.getAttachment(messageId, attachmentId, user,
                        new Handler<Either<String, JsonObject>>() {
                            @Override
                            public void handle(Either<String, JsonObject> event) {
                                if (event.isLeft()) {
                                    badRequest(request, event.left().getValue());
                                    return;
                                }
                                if (event.isRight() && event.right().getValue() == null) {
                                    badRequest(request, event.right().getValue().toString());
                                    return;
                                }

                                JsonObject neoResult = event.right().getValue();
                                String fileId = neoResult.getString("id");
                                if (fileId == null || fileId.trim().length() == 0) {
                                    notFound(request, "invalid.file.id");
                                    return;
                                }

                                JsonObject metadata = new JsonObject()
                                        .put("filename", neoResult.getString("filename"))
                                        .put("content-type", neoResult.getString("contentType"));

                                storage.sendFile(fileId, neoResult.getString("filename"), request, false, metadata);
                            }
                        });
            }
        };

        UserUtils.getUserInfos(eb, request, userInfosHandler);
    }

    //Download all attachments
    @Get("message/:id/allAttachments")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(MessageUserFilter.class)
    public void getAllAttachment(final HttpServerRequest request) {
        final String messageId = request.params().get("id");

        Handler<UserInfos> userInfosHandler = new Handler<UserInfos>() {
            public void handle(final UserInfos user) {
                if (user == null) {
                    unauthorized(request);
                    return;
                }

                conversationService.getAllAttachments(messageId, user, new Handler<Either<String, JsonArray>>() {
                    @Override
                    public void handle(Either<String, JsonArray> event) {
                        if (event.isRight()) {
                            if (event.right().getValue() == null || event.right().getValue().size() < 1) {
                                badRequest(request);
                                return;
                            }
                            if (event.right().getValue().size() < 2) {
                                JsonObject attachment = event.right().getValue().getJsonObject(0);
                                JsonObject metadata = new JsonObject()
                                        .put("filename", attachment.getString("filename"))
                                        .put("content-type", attachment.getString("contentType"));

                                storage.sendFile(attachment.getString("id"), attachment.getString("filename"),
                                        request, false, metadata);
                            } else {
                                zipAllAttachments(request, event.right().getValue());
                            }
                        } else {
                            badRequest(request, event.left().getValue());
                        }

                    }
                });
            }
        };

        UserUtils.getUserInfos(eb, request, userInfosHandler);
    }

    private void zipAllAttachments(final HttpServerRequest request, JsonArray files) {
        JsonObject tmp;
        final FileSystem fs = vertx.fileSystem();
        final List<String> fileIds = new ArrayList<>();
        final JsonObject aliasFileName = new JsonObject();
        final String zipDownloadName = I18n.getInstance().translate("attachments", I18n.DEFAULT_DOMAIN,
                I18n.acceptLanguage(request)) + ".zip";
        final String zipDirectory = exportPath + File.separator + UUID.randomUUID().toString();

        for (Object file : files) {
            tmp = (JsonObject) file;
            fileIds.add(tmp.getString("id"));
            aliasFileName.put(tmp.getString("id"), StringUtils.stripAccents(tmp.getString("filename")));
        }

        fs.mkdirs(zipDirectory, new Handler<AsyncResult<Void>>() {

            private void delete(final String path) {
                fs.deleteRecursive(path, true, new Handler<AsyncResult<Void>>() {
                    @Override
                    public void handle(AsyncResult<Void> event) {
                        if (event.failed())
                            log.error("[Conversation] Error deleting  : " + path, event.cause());
                    }
                });
            }

            @Override
            public void handle(AsyncResult<Void> event) {
                if (event.succeeded()) {
                    final String zipfile = zipDirectory + ".zip";

                    storage.writeToFileSystem(fileIds.toArray(new String[0]), zipDirectory, aliasFileName,
                            new Handler<JsonObject>() {
                                @Override
                                public void handle(JsonObject event) {
                                    if (!"ok".equals(event.getString("status"))) {
                                        log.error("[Conversation] Can't write to zip directory : "
                                                + event.getString("message"));
                                        delete(zipDirectory);
                                        badRequest(request);

                                    } else {
                                        Zip.getInstance().zipFolder(zipDirectory, zipfile, true,
                                                Deflater.NO_COMPRESSION, new Handler<Message<JsonObject>>() {
                                                    @Override
                                                    public void handle(Message<JsonObject> event) {
                                                        if (!"ok".equals(event.body().getString("status"))) {
                                                            log.error("[Conversation] Zip folder " + zipDirectory
                                                                    + " error : "
                                                                    + event.body().getString("message"));
                                                            delete(zipDirectory);
                                                            badRequest(request);
                                                        } else {
                                                            final HttpServerResponse resp = request.response();
                                                            resp.putHeader("Content-Disposition",
                                                                    "attachment; filename=\"" + zipDownloadName
                                                                            + "\"");
                                                            resp.putHeader("Content-Type",
                                                                    "application/zip; name=\"\" + zipDownloadName + \"\"");
                                                            resp.sendFile(zipfile,
                                                                    new Handler<AsyncResult<Void>>() {
                                                                        public void handle(
                                                                                AsyncResult<Void> event) {
                                                                            if (event.failed())
                                                                                log.error(
                                                                                        "Error can't send  the file: ",
                                                                                        event.cause());
                                                                            delete(zipfile);
                                                                        }
                                                                    });
                                                        }
                                                    }
                                                });
                                    }
                                }
                            });
                }
            }
        });
    }

    //Delete an attachment
    @Delete("message/:id/attachment/:attachmentId")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(MessageUserFilter.class)
    public void deleteAttachment(final HttpServerRequest request) {
        final String messageId = request.params().get("id");
        final String attachmentId = request.params().get("attachmentId");

        Handler<UserInfos> userInfosHandler = new Handler<UserInfos>() {
            public void handle(final UserInfos user) {
                if (user == null) {
                    unauthorized(request);
                    return;
                }

                conversationService.removeAttachment(messageId, attachmentId, user,
                        new Handler<Either<String, JsonObject>>() {
                            @Override
                            public void handle(Either<String, JsonObject> event) {
                                if (event.isLeft()) {
                                    badRequest(request, event.left().getValue());
                                    return;
                                }
                                if (event.isRight() && event.right().getValue() == null) {
                                    badRequest(request, event.right().getValue().toString());
                                    return;
                                }

                                final JsonObject result = event.right().getValue();

                                boolean deletionCheck = result.getBoolean("deletionCheck", false);
                                final String fileId = result.getString("fileId");
                                final long fileSize = result.getLong("fileSize");

                                updateUserQuota(user.getUserId(), -fileSize, new Handler<Void>() {
                                    public void handle(Void v) {
                                        renderJson(request, result);
                                    }
                                });

                                if (deletionCheck) {
                                    storage.removeFile(fileId, new Handler<JsonObject>() {
                                        @Override
                                        public void handle(final JsonObject result) {
                                            if (!"ok".equals(result.getString("status"))) {
                                                log.error("[" + ConversationController.class.getSimpleName()
                                                        + "] Error while tying to delete attachment file (_id: {"
                                                        + fileId + "})");
                                            }
                                        }
                                    });
                                }
                            }
                        });
            }
        };

        UserUtils.getUserInfos(eb, request, userInfosHandler);
    }

    @Put("message/:id/forward/:forwardedId")
    @SecuredAction(value = "", type = ActionType.RESOURCE)
    @ResourceFilter(MessageUserFilter.class)
    public void forwardAttachments(final HttpServerRequest request) {
        final String messageId = request.params().get("id");
        final String forwardedId = request.params().get("forwardedId");

        //1 - get user infos
        Handler<UserInfos> userInfosHandler = new Handler<UserInfos>() {
            public void handle(final UserInfos user) {
                if (user == null) {
                    unauthorized(request);
                    return;
                }
                //2 - get user quota
                getUserQuota(user.getUserId(), new Handler<JsonObject>() {
                    public void handle(JsonObject j) {
                        if (j == null || "error".equals(j.getString("status"))) {
                            badRequest(request, j == null ? "" : j.getString("message"));
                            return;
                        }

                        final long quotaLeft = j.getLong("quota", 0l) - j.getLong("storage", 0l);

                        //3 - get forwarded message attachments
                        conversationService.get(forwardedId, user, new Handler<Either<String, JsonObject>>() {
                            @Override
                            public void handle(Either<String, JsonObject> event) {
                                if (event.isLeft()) {
                                    badRequest(request, event.left().getValue());
                                    return;
                                }
                                if (event.isRight() && event.right().getValue() == null) {
                                    badRequest(request, event.right().getValue().toString());
                                    return;
                                }
                                final JsonObject neoResult = event.right().getValue();
                                final JsonArray attachments = neoResult.getJsonArray("attachments");

                                long attachmentsSize = 0l;
                                for (Object genericObj : attachments) {
                                    JsonObject attachment = (JsonObject) genericObj;
                                    attachmentsSize += attachment.getLong("size", 0l);
                                }
                                final long finalAttachmentsSize = attachmentsSize;

                                // if total attachment size > quota left, return 403
                                if (attachmentsSize > quotaLeft) {
                                    forbidden(request, "forward.failed.quota");
                                    return;
                                }

                                //4 - forward attachments, add relationships between the message and the already existing attachments
                                conversationService.forwardAttachments(forwardedId, messageId, user,
                                        new Handler<Either<String, JsonObject>>() {
                                            @Override
                                            public void handle(Either<String, JsonObject> event) {
                                                if (event.isLeft()) {
                                                    badRequest(request, event.left().getValue());
                                                    return;
                                                }

                                                //5 - update user quota
                                                updateUserQuota(user.getUserId(), finalAttachmentsSize,
                                                        new Handler<Void>() {
                                                            @Override
                                                            public void handle(Void event) {
                                                                ok(request);
                                                            }
                                                        });
                                            }

                                        });
                            }
                        });
                    }
                });
            }
        };
        UserUtils.getUserInfos(eb, request, userInfosHandler);
    }

    @Get("/print")
    @SecuredAction(value = "conversation.print", type = ActionType.AUTHENTICATED)
    public void print(final HttpServerRequest request) {
        renderView(request, null, "print.html", null);
    }

    @BusAddress("org.entcore.conversation")
    public void conversationEventBusHandler(Message<JsonObject> message) {
        switch (message.body().getString("action", "")) {
        case "send":
            send(message);
            break;
        default:
            message.reply(new JsonObject().put("status", "error").put("message", "invalid.action"));
        }
    }

    private void send(final Message<JsonObject> message) {
        JsonObject m = message.body().getJsonObject("message");
        if (m == null) {
            message.reply(new JsonObject().put("status", "error").put("message", "invalid.message"));
        }
        final HttpServerRequest request = new JsonHttpServerRequest(
                message.body().getJsonObject("request", new JsonObject()));
        final UserInfos user = new UserInfos();
        user.setUserId(message.body().getString("userId"));
        user.setUsername(message.body().getString("username"));
        if (!m.containsKey("from")) {
            m.put("from", user.getUserId());
        }
        neoConversationService.addDisplayNames(m, null, new Handler<JsonObject>() {
            public void handle(final JsonObject m) {
                saveAndSend(null, m, user, null, null, new Handler<Either<String, JsonObject>>() {
                    @Override
                    public void handle(Either<String, JsonObject> event) {
                        if (event.isRight()) {
                            JsonObject result = event.right().getValue();
                            JsonObject timelineParams = new JsonObject().put("subject", result.getString("subject"))
                                    .put("id", result.getString("id")).put("sentIds", m.getJsonArray("allUsers",
                                            new fr.wseduc.webutils.collections.JsonArray()));
                            timelineNotification(request, timelineParams, user);
                            JsonObject s = new JsonObject().put("status", "ok").put("result",
                                    new fr.wseduc.webutils.collections.JsonArray().add(new JsonObject()));
                            message.reply(s);
                        } else {
                            JsonObject error = new JsonObject().put("error", event.left().getValue());
                            message.reply(error);
                        }
                    }
                });
            }
        });

    }

    private void getUserQuota(String userId, final Handler<JsonObject> handler) {
        JsonObject message = new JsonObject();
        message.put("action", "getUserQuota");
        message.put("userId", userId);

        eb.send(QUOTA_BUS_ADDRESS, message, handlerToAsyncHandler(new Handler<Message<JsonObject>>() {
            public void handle(Message<JsonObject> reply) {
                handler.handle(reply.body());
            }
        }));
    }

    private void updateUserQuota(final String userId, long size) {
        updateUserQuota(userId, size, null);
    }

    private void updateUserQuota(final String userId, long size, final Handler<Void> continuation) {
        JsonObject message = new JsonObject();
        message.put("action", "updateUserQuota");
        message.put("userId", userId);
        message.put("size", size);
        message.put("threshold", threshold);

        eb.send(QUOTA_BUS_ADDRESS, message, handlerToAsyncHandler(new Handler<Message<JsonObject>>() {
            public void handle(Message<JsonObject> reply) {
                JsonObject obj = reply.body();
                UserUtils.addSessionAttribute(eb, userId, "storage", obj.getLong("storage"), null);
                if (obj.getBoolean("notify", false)) {
                    notifyEmptySpaceIsSmall(userId);
                }

                if (continuation != null)
                    continuation.handle(null);
            }
        }));
    }

    private void notifyEmptySpaceIsSmall(String userId) {
        List<String> recipients = new ArrayList<>();
        recipients.add(userId);
        notification.notifyTimeline(new JsonHttpServerRequest(new JsonObject()), "messagerie.storage", null,
                recipients, null, new JsonObject());
    }

}