# ScheduledAlertUser.java

    /*
     * Copyright (C) 2000 - 2024 Silverpeas
     *
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU Affero General Public License as
     * published by the Free Software Foundation, either version 3 of the
     * License, or (at your option) any later version.
     *
     * As a special exception to the terms and conditions of version 3.0 of
     * the GPL, you may redistribute this Program in connection with Free/Libre
     * Open Source Software ("FLOSS") applications as described in Silverpeas's
     * FLOSS exception. You should have received a copy of the text describing
     * the FLOSS exception, and it is also available here:
     * "http://www.silverpeas.org/docs/core/legal/floss_exception.html"
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     * GNU Affero General Public License for more details.
     *
     * You should have received a copy of the GNU Affero General Public License
     * along with this program. If not, see <https://www.gnu.org/licenses/>.
     */
    package org.silverpeas.components.gallery.service;

    import jakarta.inject.Inject;
    import org.silverpeas.components.gallery.model.GalleryRuntimeException;
    import org.silverpeas.components.gallery.model.Media;
    import org.silverpeas.core.admin.service.OrganizationController;
    import org.silverpeas.core.admin.user.model.UserDetail;
    import org.silverpeas.core.annotation.Bean;
    import org.silverpeas.core.notification.NotificationException;
    import org.silverpeas.core.notification.user.client.NotificationMetaData;
    import org.silverpeas.core.notification.user.client.NotificationSender;
    import org.silverpeas.core.notification.user.client.UserRecipient;
    import org.silverpeas.core.scheduler.*;
    import org.silverpeas.core.scheduler.trigger.JobTrigger;
    import org.silverpeas.core.ui.DisplayI18NHelper;
    import org.silverpeas.core.util.Link;
    import org.silverpeas.core.util.URLUtil;
    import org.silverpeas.kernel.bundle.LocalizationBundle;
    import org.silverpeas.kernel.bundle.ResourceLocator;
    import org.silverpeas.kernel.bundle.SettingBundle;
    import org.silverpeas.kernel.logging.SilverLogger;

    import java.text.MessageFormat;
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;

    import static org.silverpeas.core.notification.user.client.NotificationParameters.PRIORITY_NORMAL;

    @Bean
    public class ScheduledAlertUser implements SchedulerEventListener {

      public static final String GALLERYENGINE_JOB_NAME = "GalleryEngineJob";

      @Inject
      private GalleryService mediaService;

      @Inject
      private OrganizationController organizationController;

      @Inject
      private Scheduler scheduler;

      public void initialize() {
        try {
          SettingBundle resources =
              ResourceLocator.getSettingBundle("org.silverpeas.gallery.settings.gallerySettings");
          String cron = resources.getString("cronScheduledAlertUser");
          scheduler.unscheduleJob(GALLERYENGINE_JOB_NAME);
          JobTrigger trigger = JobTrigger.triggerAt(cron);
          scheduler.scheduleJob(GALLERYENGINE_JOB_NAME, trigger, this);
        } catch (Exception e) {
          SilverLogger.getLogger(this).error(
              "can not initialize successfully the batch in charge of alerting administrators about " +
                  "the end of visibility of media", e);
        }
      }

      public void release() {
        try {
          scheduler.unscheduleJob(GALLERYENGINE_JOB_NAME);
        } catch (SchedulerException e) {
          SilverLogger.getLogger(this).error(e);
        }
      }

      public void doScheduledAlertUser() {
        try {
          // Finding media for which the visibility will soon end
          SettingBundle resources =
              ResourceLocator.getSettingBundle("org.silverpeas.gallery.settings.gallerySettings");
          int nbDays = resources.getInteger("nbDaysForAlertUser");
          Collection<Media> mediaList = mediaService.getAllMediaThatWillBeNotVisible(nbDays);

          String currentInstanceId = null;
          LocalizedContent messageContent = new LocalizedContent();
          for (Media media : mediaList) {
            if (!media.getInstanceId().equals(currentInstanceId)) {
              if (currentInstanceId != null) {
                // Sending the notification
                createMessage(messageContent, currentInstanceId);
                messageContent = new LocalizedContent();
              }
              currentInstanceId = media.getInstanceId();
            }
            // Adding the media header information
            messageContent.appendFromBundleKey("gallery.notifName").append(" : ")
                .append(media.getName()).append("\n");
          }

          // Finally send the last message
          if (currentInstanceId != null) {
            createMessage(messageContent, currentInstanceId);
          }
        } catch (Exception e) {
          throw new GalleryRuntimeException(e);
        }
      }

      private void createMessage(LocalizedContent localizedContent, String componentInstanceId) {
        UserDetail[] admins = organizationController.getUsers(componentInstanceId, "admin");
        if (admins == null || admins.length == 0) {
          return;
        }

        NotificationMetaData notificationMetaData = new NotificationMetaData(PRIORITY_NORMAL, "", "");

        // Preparing the notification content
        localizedContent.append("\n").appendFromBundleKey("gallery.notifUserInfo").append("\n\n");
        mergeLocalizedContentIntoNotificationMetaData(componentInstanceId, localizedContent,
            notificationMetaData);
        notificationMetaData.setComponentId(componentInstanceId);

        // Sending the notification to the component instance administrators
        for (UserDetail admin : admins) {
          notificationMetaData.addUserRecipient(new UserRecipient(admin));
        }
        NotificationSender notifSender = new NotificationSender(componentInstanceId);
        try {
          notifSender.notifyUser(notificationMetaData);
        } catch (NotificationException e) {
          SilverLogger.getLogger(this)
              .error("can not send the notification message about media which will be no more visible",
                  e);
        }
      }

      private void mergeLocalizedContentIntoNotificationMetaData(final String componentInstanceId,
          LocalizedContent localizedContent, NotificationMetaData notificationMetaData) {
        String nameInstance =
            organizationController.getComponentInst(componentInstanceId).getLabel();
        String url = URLUtil.getComponentInstanceURL(componentInstanceId) + "Main";
        for (String language : DisplayI18NHelper.getLanguages()) {
          LocalizationBundle bundle = LocalizedContent.getBundle(language);
          String subject = bundle.getString("gallery.notifSubject");
          String body = MessageFormat
              .format("{0} <b>{1}</b>\n\n{2}", bundle.getString("gallery.notifTitle"), nameInstance,
                  localizedContent.get(language));
          notificationMetaData.addLanguage(language, subject, body);
          Link link = new Link(url, nameInstance);
          notificationMetaData.setLink(link, language);
        }
      }

      @Override
      public void triggerFired(SchedulerEvent anEvent) {
        doScheduledAlertUser();
      }

      @Override
      public void jobSucceeded(SchedulerEvent anEvent) {
        // nothing to do
      }

      @Override
      public void jobFailed(SchedulerEvent anEvent) {
        SilverLogger.getLogger(this).error(
            "The job '" + anEvent.getJobExecutionContext().getJobName() + "' was not successfully",
            anEvent.getJobThrowable());
      }

      private static class LocalizedContent {
        private final Map<String, StringBuilder> localizedContents = new HashMap<>();

        public LocalizedContent appendFromBundleKey(String key) {
          for (String language : DisplayI18NHelper.getLanguages()) {
            append(localizedContents, language, getBundle(language).getString(key));
          }
          return this;
        }

        public LocalizedContent append(String content) {
          for (String language : DisplayI18NHelper.getLanguages()) {
            append(localizedContents, language, content);
          }
          return this;
        }

        static LocalizationBundle getBundle(String language) {
          return ResourceLocator
              .getLocalizationBundle("org.silverpeas.gallery.multilang.galleryBundle", language);
        }

        private static void append(Map<String, StringBuilder> container, String language,
            String message) {
          StringBuilder sb =  container.computeIfAbsent(language, l -> new StringBuilder());
          sb.append(message);
        }

        public String get(final String language) {
          StringBuilder sb = localizedContents.get(language);
          return sb != null ? sb.toString() : "";
        }
      }
    }