org.entcore.common.notification.TimelineHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.entcore.common.notification.TimelineHelper.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.common.notification;

import fr.wseduc.webutils.Utils;
import fr.wseduc.webutils.data.FileResolver;
import fr.wseduc.webutils.http.Renders;

import io.vertx.core.shareddata.LocalMap;
import org.entcore.common.http.request.JsonHttpServerRequest;
import org.entcore.common.user.UserInfos;
import org.entcore.common.utils.StringUtils;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.Message;
import io.vertx.core.file.FileProps;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import static fr.wseduc.webutils.Utils.handlerToAsyncHandler;

public class TimelineHelper {

    private static final String TIMELINE_ADDRESS = "wse.timeline";
    private final static String messagesDir = FileResolver.absolutePath("i18n/timeline");
    private final EventBus eb;
    private final Renders render;
    private final Vertx vertx;
    private final JsonObject config;
    private final TimelineNotificationsLoader notificationsLoader;
    private static final Logger log = LoggerFactory.getLogger(TimelineHelper.class);

    public TimelineHelper(Vertx vertx, EventBus eb, JsonObject config) {
        this.eb = eb;
        this.render = new Renders(vertx, config);
        this.vertx = vertx;
        this.config = config;
        this.notificationsLoader = TimelineNotificationsLoader.getInstance(vertx);
        loadTimelineI18n();
        loadAssetsTimelineDirectory();
    }

    public void notifyTimeline(HttpServerRequest request, String notificationName, UserInfos sender,
            List<String> recipients, JsonObject params) {
        notifyTimeline(request, notificationName, sender, recipients, null, null, params);
    }

    public void notifyTimeline(HttpServerRequest request, String notificationName, UserInfos sender,
            List<String> recipients, String resource, JsonObject params) {
        notifyTimeline(request, notificationName, sender, recipients, resource, null, params);
    }

    public void notifyTimeline(HttpServerRequest request, final String notificationName, UserInfos sender,
            final List<String> recipients, String resource, String subResource, final JsonObject params) {
        notifyTimeline(request, notificationName, sender, recipients, resource, subResource, params, false);
    }

    public void notifyTimeline(final HttpServerRequest req, final String notificationName, UserInfos sender,
            final List<String> recipients, String resource, String subResource, final JsonObject params,
            final boolean disableAntiFlood) {
        notificationsLoader.getNotification(notificationName, notification -> {
            JsonArray r = new fr.wseduc.webutils.collections.JsonArray();
            for (String userId : recipients) {
                r.add(new JsonObject().put("userId", userId).put("unread", 1));
            }
            final JsonObject event = new JsonObject().put("action", "add")
                    .put("type", notification.getString("type"))
                    .put("event-type", notification.getString("event-type")).put("recipients", r)
                    .put("recipientsIds", new fr.wseduc.webutils.collections.JsonArray(recipients));
            if (resource != null) {
                event.put("resource", resource);
            }
            if (sender != null) {
                event.put("sender", sender.getUserId());
            }
            if (subResource != null && !subResource.trim().isEmpty()) {
                event.put("sub-resource", subResource);
            }
            if (disableAntiFlood || params.getBoolean("disableAntiFlood", false)) {
                event.put("disableAntiFlood", true);
            }
            Long date = params.getLong("timeline-publish-date");
            if (date != null) {
                event.put("date", new JsonObject().put("$date", date));
                params.remove("timeline-publish-date");
            }

            event.put("pushNotif", params.remove("pushNotif"));

            HttpServerRequest request;
            if (req == null) {
                request = new JsonHttpServerRequest(new JsonObject());
            } else {
                request = req;
            }
            event.put("params", params).put("notificationName", notificationName).put("notification", notification)
                    .put("request",
                            new JsonObject().put("headers",
                                    new JsonObject().put("Host", Renders.getHost(request))
                                            .put("X-Forwarded-Proto", Renders.getScheme(request))
                                            .put("Accept-Language", request.headers().get("Accept-Language"))));
            eb.send(TIMELINE_ADDRESS, event, handlerToAsyncHandler(new Handler<Message<JsonObject>>() {
                public void handle(Message<JsonObject> event) {
                    JsonObject result = event.body();
                    if ("error".equals(result.getString("status", "error"))) {
                        log.error("Error in timeline notification : " + result.getString("message"));
                    }
                }
            }));
        });
    }

    /**
     * @deprecated
     * Notification system was refactored in version 1.16.1
     */
    @Deprecated
    public void notifyTimeline(HttpServerRequest request, UserInfos sender, String type, String eventType,
            List<String> recipients, String resource, String template, JsonObject params) {
        notifyTimeline(request, sender, type, eventType, recipients, resource, null, template, params);
    }

    /**
     * @deprecated
     * Notification system was refactored in version 1.16.1
     */
    @Deprecated
    public void notifyTimeline(HttpServerRequest request, UserInfos sender, String type, final String eventType,
            List<String> recipients, String resource, String subResource, String template, JsonObject params) {
        JsonArray r = new fr.wseduc.webutils.collections.JsonArray();
        for (String userId : recipients) {
            r.add(new JsonObject().put("userId", userId).put("unread", 1));
        }
        final JsonObject event = new JsonObject().put("action", "add").put("type", type)
                .put("event-type", eventType).put("recipients", r);
        if (resource != null) {
            event.put("resource", resource);
        }
        if (sender != null) {
            event.put("sender", sender.getUserId());
        }
        if (subResource != null && !subResource.trim().isEmpty()) {
            event.put("sub-resource", subResource);
        }
        Long date = params.getLong("timeline-publish-date");
        if (date != null) {
            event.put("date", new JsonObject().put("$date", date));
            params.remove("timeline-publish-date");
        }
        render.processTemplate(request, template, params, new Handler<String>() {
            @Override
            public void handle(String message) {
                if (message != null) {
                    event.put("message", message);
                    eb.send(TIMELINE_ADDRESS, event);
                } else {
                    log.error("Unable to send timeline " + eventType + " notification.");
                }
            }
        });
    }

    private void loadTimelineI18n() {
        vertx.fileSystem().exists(messagesDir, new Handler<AsyncResult<Boolean>>() {
            @Override
            public void handle(AsyncResult<Boolean> ar) {
                if (ar.succeeded() && ar.result()) {
                    vertx.fileSystem().readDir(messagesDir, new Handler<AsyncResult<List<String>>>() {
                        @Override
                        public void handle(AsyncResult<List<String>> ar) {
                            if (ar.succeeded()) {
                                readI18nTimeline(ar);
                            }
                        }
                    });
                } else {
                    log.warn("I18n directory " + messagesDir + " doesn't exist.");
                }
            }
        });
    }

    private void readI18nTimeline(AsyncResult<List<String>> ar) {
        final Map<String, JsonObject> messages = new HashMap<>();
        final AtomicInteger count = new AtomicInteger(ar.result().size());
        for (final String path : ar.result()) {
            vertx.fileSystem().props(path, new Handler<AsyncResult<FileProps>>() {
                @Override
                public void handle(AsyncResult<FileProps> ar) {
                    if (ar.succeeded() && ar.result().isRegularFile()) {
                        final String k = new File(path).getName().split("\\.")[0];
                        vertx.fileSystem().readFile(path, new Handler<AsyncResult<Buffer>>() {
                            @Override
                            public void handle(AsyncResult<Buffer> ar) {
                                if (ar.succeeded()) {
                                    JsonObject jo = new JsonObject(ar.result().toString("UTF-8"));
                                    messages.put(k, jo);
                                }
                                if (count.decrementAndGet() == 0) {
                                    appendTimelineEventsI18n(messages);
                                }
                            }
                        });
                    } else {
                        if (count.decrementAndGet() == 0) {
                            appendTimelineEventsI18n(messages);
                        }
                    }
                }
            });
        }
    }

    private void loadAssetsTimelineDirectory() {
        final String[] app = Utils.loadFromResource("mod.json").getString("main").split("\\.");
        final String assetsDirectory = config.getString("assets-path", "../..") + File.separator + "assets";
        final String i18nDirectory = assetsDirectory + File.separator + "i18n" + File.separator
                + app[app.length - 1] + File.separator + "timeline";
        vertx.fileSystem().exists(i18nDirectory, new Handler<AsyncResult<Boolean>>() {
            @Override
            public void handle(AsyncResult<Boolean> ar) {
                if (ar.succeeded() && ar.result()) {
                    vertx.fileSystem().readDir(i18nDirectory, new Handler<AsyncResult<List<String>>>() {
                        @Override
                        public void handle(AsyncResult<List<String>> asyncResult) {
                            if (asyncResult.succeeded()) {
                                readI18nTimeline(asyncResult);
                            } else {
                                log.error("Error loading assets i18n timeline.", asyncResult.cause());
                            }
                        }
                    });
                } else if (ar.failed()) {
                    log.error("Error loading assets i18n timeline.", ar.cause());
                }
            }
        });
    }

    private void appendTimelineEventsI18n(Map<String, JsonObject> i18ns) {
        LocalMap<String, String> eventsI18n = vertx.sharedData().getLocalMap("timelineEventsI18n");
        for (Map.Entry<String, JsonObject> e : i18ns.entrySet()) {
            String json = e.getValue().encode();
            if (StringUtils.isEmpty(json) || "{}".equals(StringUtils.stripSpaces(json)))
                continue;
            String j = json.substring(1, json.length() - 1) + ",";
            String resJson = j;
            String oldJson = eventsI18n.putIfAbsent(e.getKey(), j);
            if (oldJson != null && !oldJson.equals(j)) {
                resJson += oldJson;
                boolean appended = eventsI18n.replace(e.getKey(), oldJson, resJson);
                while (!appended) {
                    oldJson = eventsI18n.get(e.getKey());
                    resJson = j;
                    resJson += oldJson;
                    appended = eventsI18n.replace(e.getKey(), oldJson, resJson);
                }
            }
        }
    }

}