diff --git a/component/api/pom.xml b/component/api/pom.xml
index aebe76911ca..c3f3acf9629 100644
--- a/component/api/pom.xml
+++ b/component/api/pom.xml
@@ -57,11 +57,6 @@
org.exoplatform.commonscommons-comet-service
-
org.exoplatform.commons
diff --git a/component/notification/pom.xml b/component/notification/pom.xml
index e97755537c9..cecd7e521cb 100644
--- a/component/notification/pom.xml
+++ b/component/notification/pom.xml
@@ -25,7 +25,6 @@
org.exoplatform.social6.5.x-SNAPSHOT
- org.exoplatform.socialsocial-component-notificationeXo PLF:: Social Notification ComponenteXo Social Notification Component
@@ -58,9 +57,11 @@
org.apache.maven.pluginsmaven-surefire-plugin
+ alphabetical**/InitContainerTestSuite.java**/InitContainerWithSettingsTestSuite.java
+ **/InitContainerTestSuiteRest.java
diff --git a/component/notification/src/main/java/io/meeds/social/notification/rest/NotificationSettingsRestService.java b/component/notification/src/main/java/io/meeds/social/notification/rest/NotificationSettingsRestService.java
new file mode 100644
index 00000000000..bc28d8dc966
--- /dev/null
+++ b/component/notification/src/main/java/io/meeds/social/notification/rest/NotificationSettingsRestService.java
@@ -0,0 +1,554 @@
+/**
+ * This file is part of the Meeds project (https://meeds.io/).
+ *
+ * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ * 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
+ * Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package io.meeds.social.notification.rest;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.annotation.security.RolesAllowed;
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.ArrayUtils;
+
+import org.exoplatform.commons.api.notification.channel.AbstractChannel;
+import org.exoplatform.commons.api.notification.channel.ChannelManager;
+import org.exoplatform.commons.api.notification.model.*;
+import org.exoplatform.commons.api.notification.model.UserSetting.FREQUENCY;
+import org.exoplatform.commons.api.notification.plugin.config.PluginConfig;
+import io.meeds.social.notification.rest.model.ChannelActivationChoice;
+import io.meeds.social.notification.rest.model.EmailDigestChoice;
+import io.meeds.social.notification.rest.model.UserNotificationSettings;
+import org.exoplatform.commons.api.notification.service.setting.PluginSettingService;
+import org.exoplatform.commons.api.notification.service.setting.UserSettingService;
+import org.exoplatform.portal.application.localization.LocalizationFilter;
+import org.exoplatform.portal.config.UserACL;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+import org.exoplatform.services.resources.ResourceBundleService;
+import org.exoplatform.services.rest.resource.ResourceContainer;
+import org.exoplatform.services.security.ConversationState;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
+import org.exoplatform.services.rest.http.PATCH;
+
+@Path("notifications/settings")
+@Tag(name = "notifications/settings", description = "Managing users notifications settings")
+public class NotificationSettingsRestService implements ResourceContainer {
+
+ private static final String NOTIFICATION_LABEL_CHANNEL_DEFAULT = "UINotification.label.channel.default";
+
+ private static final String MAIN_RESOURCE_BUNDLE_NAME = "locale.portlet.UserNotificationPortlet";
+
+ private static final Log LOG = ExoLogger.getLogger(NotificationSettingsRestService.class);
+
+ private static final String DAILY = "Daily";
+
+ private static final String WEEKLY = "Weekly";
+
+ private static final String NEVER = "Never";
+
+ private ResourceBundleService resourceBundleService;
+
+ private PluginSettingService pluginSettingService;
+
+ private ChannelManager channelManager;
+
+ private UserSettingService userSettingService;
+
+ private UserACL userACL;
+
+ public NotificationSettingsRestService(ResourceBundleService resourceBundleService,
+ PluginSettingService pluginSettingService,
+ ChannelManager channelManager,
+ UserSettingService userSettingService,
+ UserACL userACL) {
+ this.resourceBundleService = resourceBundleService;
+ this.pluginSettingService = pluginSettingService;
+ this.channelManager = channelManager;
+ this.userSettingService = userSettingService;
+ this.userACL = userACL;
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @RolesAllowed("administrators")
+ @Operation(
+ summary = "Get default notification settings",
+ description = "Get default notification settings",
+ method = "GET")
+ @ApiResponses(
+ value = {
+ @ApiResponse(responseCode = "200", description = "Request fulfilled"),
+ }
+ )
+ public Response getSettings() {
+ UserSetting setting = userSettingService.getDefaultSettings();
+ UserNotificationSettings notificationSettings = mapToRestModel(setting, false);
+ notificationSettings.setSenderName(pluginSettingService.getEmailSenderName());
+ notificationSettings.setSenderEmail(pluginSettingService.getEmailSenderEmail());
+ return Response.ok(notificationSettings).build();
+ }
+
+ @PATCH
+ @RolesAllowed("administrators")
+ @Operation(
+ summary = "Change enablement status of Channel for all users",
+ description = "Change enablement status of Channel for all users",
+ method = "PATCH"
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(responseCode = "204", description = "Request fulfilled")
+ }
+ )
+ public Response saveEmailSender(
+ @Parameter(description = "Company name", required = true)
+ @FormParam("name")
+ String name,
+ @Parameter(description = "Company email", required = true)
+ @FormParam("email")
+ String email) {
+ try {
+ pluginSettingService.saveEmailSender(name, email);
+ return Response.noContent().build();
+ } catch (Exception e) {
+ return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
+ }
+ }
+
+ @GET
+ @Path("{id}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @RolesAllowed("users")
+ @Operation(
+ summary = "Gets all notification settings of a user",
+ description = "Gets all notification settings of a user",
+ method = "GET")
+ @ApiResponses(
+ value = {
+ @ApiResponse(responseCode = "200", description = "Request fulfilled"),
+ @ApiResponse(responseCode = "500", description = "Internal server error")
+ }
+ )
+ public Response getSettings(
+ @Parameter(
+ description = "User name that will be used to retrieve its settings. "
+ + "If current user is and administrator, it will be able to retrieve settings of all users",
+ required = true
+ ) @PathParam("id") String username) {
+ boolean isAdmin = userACL.isSuperUser() || userACL.isUserInGroup(userACL.getAdminGroups());
+ boolean isSameUser = ConversationState.getCurrent().getIdentity().getUserId().equals(username);
+ if (!isAdmin && !isSameUser) {
+ return Response.status(Response.Status.UNAUTHORIZED).build();
+ }
+ UserSetting setting = userSettingService.get(username);
+ UserNotificationSettings notificationSettings = mapToRestModel(setting, true);
+ return Response.ok(notificationSettings).build();
+ }
+
+ @PATCH
+ @Path("plugin/{pluginId}")
+ @RolesAllowed("administrators")
+ @Operation(
+ summary = "Change enablement status of Channel for all users",
+ description = "Change enablement status of Channel for all users",
+ method = "PATCH"
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(responseCode = "204", description = "Request fulfilled")
+ }
+ )
+ public Response savePluginSetting(
+ @Parameter(description = "Notification plugin Id", required = true)
+ @PathParam("pluginId")
+ String pluginId,
+ @Parameter(description = "Notification digest to use for corresponding plugin Id", required = true)
+ @FormParam("channels")
+ String channels) {
+ String[] channelsArray = StringUtils.split(channels, ',');
+ Map channelsStatus =
+ Arrays.stream(channelsArray)
+ .collect(Collectors.toMap(channelStatus -> StringUtils.split(channelStatus, "=")[0],
+ channelStatus -> StringUtils.split(channelStatus, "=")[1]));
+ for (Map.Entry channelByStatus : channelsStatus.entrySet()) {
+ String channelId = channelByStatus.getKey();
+ boolean channelActive = Boolean.parseBoolean(channelByStatus.getValue());
+ pluginSettingService.saveActivePlugin(channelId, pluginId, channelActive);
+ }
+ return Response.noContent().build();
+ }
+
+ @PATCH
+ @Path("{id}/plugin/{pluginId}")
+ @RolesAllowed("users")
+ @Operation(
+ summary = "Change enablement status of Channel for a user",
+ description = "Change enablement status of Channel for a user",
+ method = "PATCH")
+ @ApiResponses(
+ value = {
+ @ApiResponse(responseCode = "204", description = "Request fulfilled"),
+ @ApiResponse(responseCode = "500", description = "Internal server error")
+ }
+ )
+ public Response savePluginSetting(
+ @Parameter(
+ description = "User name that will be used to save its settings.",
+ required = true
+ ) @PathParam("id") String username,
+ @Parameter(
+ description = "Notification plugin Id",
+ required = true
+ ) @PathParam("pluginId") String pluginId,
+ @Parameter(
+ description = "Notification digest to use for corresponding plugin Id",
+ required = true
+ ) @FormParam("channels") String channels,
+ @Parameter(
+ description = "Notification digest to use for corresponding plugin Id",
+ required = true
+ ) @FormParam("digest") String digest) {
+
+ boolean isAdmin = userACL.isSuperUser() || userACL.isUserInGroup(userACL.getAdminGroups());
+ boolean isSameUser = ConversationState.getCurrent().getIdentity().getUserId().equals(username);
+ if (!isAdmin && !isSameUser) {
+ return Response.status(Response.Status.UNAUTHORIZED).build();
+ }
+
+ try {
+ UserSetting setting = userSettingService.get(username);
+
+ // digest
+ if (WEEKLY.equals(digest)) {
+ setting.addPlugin(pluginId, FREQUENCY.WEEKLY);
+ } else if (DAILY.equals(digest)) {
+ setting.addPlugin(pluginId, FREQUENCY.DAILY);
+ } else {
+ setting.removePlugin(pluginId, FREQUENCY.WEEKLY);
+ setting.removePlugin(pluginId, FREQUENCY.DAILY);
+ }
+
+ // channels
+ String[] channelsArray = StringUtils.split(channels, ',');
+ Map channelsStatus = Arrays.stream(channelsArray)
+ .collect(Collectors.toMap(channelStatus -> StringUtils.split(channelStatus,
+ "=")[0],
+ channelStatus -> StringUtils.split(channelStatus,
+ "=")[1]));
+ for (Map.Entry channelByStatus : channelsStatus.entrySet()) {
+ if (Boolean.parseBoolean(channelByStatus.getValue())) {
+ setting.addChannelPlugin(channelByStatus.getKey(), pluginId);
+ } else {
+ setting.removeChannelPlugin(channelByStatus.getKey(), pluginId);
+ }
+ }
+ userSettingService.save(setting);
+ } catch (Exception e) {
+ return Response.serverError().entity("Exception in switching state of plugin " + pluginId + ". " + e.toString()).build();
+ }
+ return Response.noContent().build();
+ }
+
+ @PATCH
+ @Path("channel/{channelId}")
+ @RolesAllowed("administrators")
+ @Operation(
+ summary = "Change enablement status of Channel for all users",
+ description = "Change enablement status of Channel for all users",
+ method = "PATCH"
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(responseCode = "204", description = "Request fulfilled")
+ }
+ )
+ public Response saveChannelStatus(
+ @Parameter(description = "Channel Id like MAIL_CHANNEL, WEB_CHANNEL...", required = true)
+ @PathParam("channelId")
+ String channelId,
+ @Parameter(description = "Enable/disable a channel", required = true)
+ @FormParam("enable")
+ boolean enable) {
+ pluginSettingService.saveChannelStatus(channelId, enable);
+ return Response.noContent().build();
+ }
+
+ @PATCH
+ @Path("{id}/channel/{channelId}")
+ @RolesAllowed("users")
+ @Operation(
+ summary = "Change enablement status of Channel for a user",
+ description = "Change enablement status of Channel for a user",
+ method = "PATCH")
+ @ApiResponses(
+ value = {
+ @ApiResponse(responseCode = "204", description = "Request fulfilled"),
+ @ApiResponse(responseCode = "500", description = "Internal server error")
+ }
+ )
+ public Response saveActiveStatus(
+ @Parameter(
+ description = "User name that will be used to save its settings.",
+ required = true
+ ) @PathParam("id") String username,
+ @Parameter(
+ description = "Channel Id like MAIL_CHANNEL, WEB_CHANNEL...",
+ required = true
+ ) @PathParam("channelId") String channelId,
+ @Parameter(
+ description = "Enable/disable a channel",
+ required = true
+ ) @FormParam("enable") boolean enable) {
+
+ boolean isAdmin = userACL.isSuperUser() || userACL.isUserInGroup(userACL.getAdminGroups());
+ boolean isSameUser = ConversationState.getCurrent().getIdentity().getUserId().equals(username);
+ if (!isAdmin && !isSameUser) {
+ return Response.status(Response.Status.UNAUTHORIZED).build();
+ }
+
+ try {
+ UserSetting setting = userSettingService.get(username);
+ if (enable) {
+ setting.setChannelActive(channelId);
+ } else {
+ setting.removeChannelActive(channelId);
+ }
+ userSettingService.save(setting);
+ } catch (Exception e) {
+ return Response.serverError().entity("Exception in switching state of provider " + channelId + ". " + e.toString()).build();
+ }
+ return Response.noContent().build();
+ }
+
+ private Map buildDigestDescriptions(Context context) {
+ Map options = new HashMap<>();
+ options.put(DAILY, context.appRes("UINotification.description.Daily"));
+ options.put(WEEKLY, context.appRes("UINotification.description.Weekly"));
+ return options;
+ }
+
+ private Map buildDigestLabels(Context context) {
+ Map options = new HashMap<>();
+ options.put(DAILY, context.appRes("UINotification.label.Daily"));
+ options.put(WEEKLY, context.appRes("UINotification.label.Weekly"));
+ options.put(NEVER, context.appRes("UINotification.label.Never"));
+ return options;
+ }
+
+ private List getChannels(boolean activeChannelsOnly) {
+ List channels = new ArrayList<>();
+ for (AbstractChannel channel : channelManager.getChannels()) {
+ String channelId = channel.getId();
+ if (!activeChannelsOnly || pluginSettingService.isChannelActive(channelId)) {
+ channels.add(channelId);
+ }
+ }
+ return channels;
+ }
+
+ private Map computeChannelStatuses(UserSetting setting, List channels) {
+ Map channelStatus = new HashMap<>();
+ for (String channelId : channels) {
+ channelStatus.put(channelId, setting != null && setting.isChannelGloballyActive(channelId));
+ }
+ return channelStatus;
+ }
+
+ private boolean computeChoices(UserSetting userSetting, // NOSONAR
+ List channels,
+ List groups,
+ Map channelStatus,
+ List emailDigestChoices,
+ List channelCheckBoxList) {
+ boolean hasActivePlugin = false;
+ for (GroupProvider groupProvider : groups) {
+ for (PluginInfo pluginInfo : groupProvider.getPluginInfos()) {
+ String pluginId = pluginInfo.getType();
+ for (String channelId : channels) {
+ hasActivePlugin = true;
+ boolean isChannelActive = channelStatus.get(channelId)
+ && pluginSettingService.isActive(channelId, pluginId)
+ && userSetting.isChannelGloballyActive(channelId);
+ channelCheckBoxList.add(new ChannelActivationChoice(channelId,
+ pluginId,
+ pluginSettingService.isAllowed(channelId, pluginId),
+ isChannelActive && userSetting.isActive(channelId, pluginId),
+ isChannelActive));
+ if (UserSetting.EMAIL_CHANNEL.equals(channelId)) {
+ emailDigestChoices.add(new EmailDigestChoice(channelId,
+ pluginId,
+ getValue(userSetting, pluginId),
+ isChannelActive));
+ }
+ }
+ }
+ }
+ return hasActivePlugin;
+ }
+
+ private String getValue(UserSetting setting, String pluginId) {
+ if (setting != null && setting.isInWeekly(pluginId)) {
+ return WEEKLY;
+ } else if (setting != null && setting.isInDaily(pluginId)) {
+ return DAILY;
+ } else {
+ return NEVER;
+ }
+ }
+
+ private UserNotificationSettings mapToRestModel(UserSetting setting, boolean activeChannelsOnly) {
+ Locale userLocale = LocalizationFilter.getCurrentLocale();
+ if (userLocale == null) {
+ userLocale = Locale.ENGLISH;
+ }
+
+ String[] sharedResourceBundles = resourceBundleService.getSharedResourceBundleNames();
+ String[] resourceBundles = ArrayUtils.insert(0, sharedResourceBundles, MAIN_RESOURCE_BUNDLE_NAME);
+ ResourceBundle resourceBundle = resourceBundleService.getResourceBundle(resourceBundles, userLocale);
+ Context context = new Context(resourceBundle, userLocale);
+
+ List channels = getChannels(activeChannelsOnly);
+ Map channelStatus = computeChannelStatuses(setting, channels);
+ List groups = pluginSettingService.getGroupPlugins();
+ Map groupsLabels = groups.stream()
+ .collect(Collectors.toMap(GroupProvider::getGroupId,
+ group -> context.pluginRes(group.getResourceBundleKey(),
+ group.getGroupId())));
+ Map pluginLabels = groups.stream()
+ .flatMap(group -> group.getPluginInfos().stream())
+ .collect(Collectors.toMap(PluginInfo::getType,
+ plugin -> context.pluginRes("UINotification.title."
+ + plugin.getType(), plugin.getType())));
+ Map channelLabels = channels.stream().collect(Collectors.toMap(Function.identity(), channel -> {
+ String channelKey = context.getChannelKey(channel);
+ String key = "UINotification.label.channel-" + channelKey;
+ if (resourceBundle != null && resourceBundle.containsKey(key)) {
+ return resourceBundle.getString(key);
+ } else if (resourceBundle != null && resourceBundle.containsKey(NOTIFICATION_LABEL_CHANNEL_DEFAULT)) {
+ return resourceBundle.getString(NOTIFICATION_LABEL_CHANNEL_DEFAULT).replace("{0}", channelKey);
+ }
+ return channelKey;
+ }));
+ Map channelDescriptions = channels.stream().collect(Collectors.toMap(Function.identity(), channel -> {
+ String channelKey = context.getChannelKey(channel);
+ String key = "UINotification.description.channel-" + channelKey;
+ if (resourceBundle != null && resourceBundle.containsKey(key)) {
+ return resourceBundle.getString(key);
+ } else if (resourceBundle != null && resourceBundle.containsKey("UINotification.description.channel.default")) {
+ return resourceBundle.getString("UINotification.description.channel.default").replace("{0}", channelKey);
+ }
+ return StringUtils.EMPTY;
+ }));
+
+ Map digestLabels = buildDigestLabels(context);
+ Map digestDescriptions = buildDigestDescriptions(context);
+ List emailDigestChoices = new ArrayList<>();
+ List channelCheckBoxList = new ArrayList<>();
+ boolean hasActivePlugin = computeChoices(setting, channels, groups, channelStatus, emailDigestChoices, channelCheckBoxList);
+
+ return new UserNotificationSettings(groups,
+ groupsLabels,
+ pluginLabels,
+ channelLabels,
+ channelDescriptions,
+ digestLabels,
+ digestDescriptions,
+ hasActivePlugin,
+ emailDigestChoices,
+ channelCheckBoxList,
+ channelStatus,
+ channels);
+ }
+
+ public class Context {
+ ResourceBundle resourceBundle;
+
+ Locale userLocale;
+
+ public Context(ResourceBundle resourceBundle, Locale userLocale) {
+ this.resourceBundle = resourceBundle;
+ this.userLocale = userLocale;
+ }
+
+ public String appRes(String key) {
+ try {
+ return resourceBundle.getString(key).replace("'", "'").replace("\"", """);
+ } catch (java.util.MissingResourceException e) {
+ if (key.indexOf("checkbox-") > -1) {
+ return appRes("UINotification.label.checkbox.default").replace("{0}", capitalizeFirstLetter(key.split("-")[1]));
+ }
+ if (key.indexOf("channel-") > -1) {
+ return appRes(NOTIFICATION_LABEL_CHANNEL_DEFAULT).replace("{0}", capitalizeFirstLetter(key.split("-")[1]));
+ }
+ } catch (Exception e) {
+ LOG.debug("Error when get resource bundle key " + key, e);
+ }
+ return capitalizeFirstLetter(key.substring(key.lastIndexOf('.') + 1));
+ }
+
+ private String getBundlePath(String id, String key) { // NOSONAR
+ PluginConfig pluginConfig = pluginSettingService.getPluginConfig(id);
+ if (pluginConfig != null) {
+ return pluginConfig.getBundlePath();
+ }
+ //
+ List groups = pluginSettingService.getGroupPlugins();
+ for (GroupProvider groupProvider : groups) {
+ List pluginInfos = groupProvider.getPluginInfos();
+ if (groupProvider.getGroupId().equals(id) && pluginInfos != null && !pluginInfos.isEmpty()) {
+ for (PluginInfo pluginInfo : pluginInfos) {
+ if (StringUtils.isNotBlank(pluginInfo.getBundlePath())) {
+ ResourceBundle resBundle = resourceBundleService.getResourceBundle(pluginInfo.getBundlePath(), userLocale);
+ if (resBundle != null && resBundle.containsKey(key)) {
+ return pluginInfo.getBundlePath();
+ }
+ }
+ }
+ }
+ }
+
+ PluginConfig defaultPluginConfig = pluginSettingService.getPluginConfig("DigestDailyPlugin");
+ return defaultPluginConfig == null ? null : defaultPluginConfig.getBundlePath();
+ }
+
+ public String pluginRes(String key, String id) {
+ String path = getBundlePath(id, key);
+ ResourceBundle pluginResourceBundle = StringUtils.isBlank(path) ? null
+ : resourceBundleService.getResourceBundle(path, userLocale);
+ return pluginResourceBundle != null && pluginResourceBundle.containsKey(key) ? pluginResourceBundle.getString(key) : id;
+ }
+
+ public String getChannelKey(String channelId) {
+ return channelId.replace("_CHANNEL", "").toLowerCase();
+ }
+
+ public String capitalizeFirstLetter(String original) {
+ return original.length() <= 1 ? original : original.substring(0, 1).toUpperCase() + original.substring(1);
+ }
+ }
+
+}
diff --git a/component/notification/src/main/java/io/meeds/social/notification/rest/WebNotificationRestService.java b/component/notification/src/main/java/io/meeds/social/notification/rest/WebNotificationRestService.java
new file mode 100644
index 00000000000..27976e5fdd4
--- /dev/null
+++ b/component/notification/src/main/java/io/meeds/social/notification/rest/WebNotificationRestService.java
@@ -0,0 +1,166 @@
+/**
+ * This file is part of the Meeds project (https://meeds.io/).
+ *
+ * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ * 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
+ * Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package io.meeds.social.notification.rest;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.exoplatform.services.rest.http.PATCH;
+import java.util.List;
+
+import javax.annotation.security.RolesAllowed;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.exoplatform.commons.api.notification.model.NotificationInfo;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import org.exoplatform.commons.api.notification.NotificationMessageUtils;
+import org.exoplatform.commons.api.notification.model.WebNotificationFilter;
+import org.exoplatform.commons.api.notification.service.WebNotificationService;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+import org.exoplatform.services.rest.resource.ResourceContainer;
+import org.exoplatform.services.security.ConversationState;
+
+/**
+ * Provides REST Services in order to perform all read/write operations related
+ * to web notifications.
+ */
+
+@Path("notifications/webNotifications")
+@Tag(name = "notifications/webNotifications", description = "Manage web notifications")
+public class WebNotificationRestService implements ResourceContainer {
+
+ private static final Log LOG = ExoLogger.getLogger(WebNotificationRestService.class);
+
+ private WebNotificationService webNftService;
+
+ public WebNotificationRestService(WebNotificationService webNftService) {
+ this.webNftService = webNftService;
+ }
+
+ @GET
+ @RolesAllowed("users")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Operation(
+ summary = "Get notifications list",
+ description = "This gets the list of the notifications",
+ method = "GET")
+ @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Notifications list returned"),
+ @ApiResponse(responseCode = "404", description = "Notifications list not found"),
+ @ApiResponse(responseCode = "500", description = "Internal server error") })
+ public Response getNotifications() {
+ int maxItemsInPopover = NotificationMessageUtils.getMaxItemsInPopover();
+ JSONArray notificationsJsonArray = new JSONArray();
+ JSONObject response = new JSONObject();
+ String currentUser = ConversationState.getCurrent().getIdentity().getUserId();
+ if (currentUser == null) {
+ return Response.status(Response.Status.UNAUTHORIZED).build();
+ }
+ List notifications = webNftService.get(new WebNotificationFilter(currentUser, true), 0, maxItemsInPopover);
+ for (String notification : notifications) {
+ JSONObject notificationJsonObject = new JSONObject();
+ notificationJsonObject.put("notification", notification);
+ notificationsJsonArray.put(notificationJsonObject);
+ }
+ int badge = webNftService.getNumberOnBadge(currentUser);
+ response.put("notifications", notificationsJsonArray);
+ response.put("badge", badge);
+ return Response.ok(response.toString(), MediaType.APPLICATION_JSON).build();
+ }
+
+ @PATCH
+ @Path("{id}")
+ @Consumes(MediaType.TEXT_PLAIN)
+ @RolesAllowed("users")
+ @Operation(
+ summary = "Update notification",
+ description = "Perform some patch operations on notifications",
+ method = "PATCH")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Notification updated"),
+ @ApiResponse(responseCode = "400", description = "Invalid query input"),
+ @ApiResponse(responseCode = "500", description = "Internal server error") })
+ public Response updateNotifications(@Parameter(description = "notification operation", required = true) String operation,
+ @Parameter(description = "notification id", required = true) @PathParam("id") String notificationId) {
+
+ String currentUser = ConversationState.getCurrent().getIdentity().getUserId();
+ try {
+ if (operation == null) {
+ LOG.warn("Notification operation should be not null");
+ return Response.status(Response.Status.BAD_REQUEST).build();
+ }
+
+ if (currentUser == null) {
+ return Response.status(Response.Status.UNAUTHORIZED).build();
+ }
+
+ if (operation.equals("markAsRead")) {
+ if (notificationId == null) {
+ return Response.status(Response.Status.BAD_REQUEST).build();
+ } else {
+ NotificationInfo notification = webNftService.getNotificationInfo(notificationId);
+ if (currentUser.equals(notification.getTo())) {
+ webNftService.markRead(notificationId);
+ } else {
+ LOG.warn("User {} is not allowed to mark notification {} as read", currentUser, notificationId);
+ return Response.status(Response.Status.UNAUTHORIZED).build();
+
+ }
+ }
+ }
+
+ if (operation.equals("hide")) {
+ if (notificationId == null) {
+ return Response.status(Response.Status.BAD_REQUEST).build();
+ } else {
+ NotificationInfo notification = webNftService.getNotificationInfo(notificationId);
+ if (currentUser.equals(notification.getTo())) {
+ webNftService.hidePopover(notificationId);
+ } else {
+ LOG.warn("User {} is not allowed to hide notification {}", currentUser, notificationId);
+ return Response.status(Response.Status.UNAUTHORIZED).build();
+
+ }
+ }
+ }
+
+ if (operation.equals("resetNew")) {
+ webNftService.resetNumberOnBadge(currentUser);
+ }
+
+ if (operation.equals("markAllAsRead")) {
+ webNftService.markAllRead(currentUser);
+ webNftService.resetNumberOnBadge(currentUser);
+ }
+ return Response.noContent().build();
+ } catch (Exception e) {
+ LOG.error("Error when trying to patch operation {} on notification {} for user {}", operation, notificationId, currentUser, e);
+ return Response.serverError().build();
+ }
+ }
+}
diff --git a/component/notification/src/main/java/io/meeds/social/notification/rest/model/ChannelActivationChoice.java b/component/notification/src/main/java/io/meeds/social/notification/rest/model/ChannelActivationChoice.java
new file mode 100644
index 00000000000..8d1d38e94f2
--- /dev/null
+++ b/component/notification/src/main/java/io/meeds/social/notification/rest/model/ChannelActivationChoice.java
@@ -0,0 +1,84 @@
+/**
+ * This file is part of the Meeds project (https://meeds.io/).
+ *
+ * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ * 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
+ * Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package io.meeds.social.notification.rest.model;
+
+public class ChannelActivationChoice {
+
+ private String channelId;
+
+ private String pluginId;
+
+ private boolean allowed;
+
+ private boolean active;
+
+ private boolean channelActive;
+
+ public ChannelActivationChoice(String channelId,
+ String pluginId,
+ boolean allowed,
+ boolean active,
+ boolean channelActive) {
+ this.channelId = channelId;
+ this.pluginId = pluginId;
+ this.allowed = allowed;
+ this.active = active;
+ this.channelActive = channelActive;
+ }
+
+ public String getChannelId() {
+ return channelId;
+ }
+
+ public void setChannelId(String channelId) {
+ this.channelId = channelId;
+ }
+
+ public String getPluginId() {
+ return pluginId;
+ }
+
+ public void setPluginId(String pluginId) {
+ this.pluginId = pluginId;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+
+ public boolean isAllowed() {
+ return allowed;
+ }
+
+ public void setAllowed(boolean allowed) {
+ this.allowed = allowed;
+ }
+
+ public boolean isChannelActive() {
+ return channelActive;
+ }
+
+ public void setChannelActive(boolean channelActive) {
+ this.channelActive = channelActive;
+ }
+
+}
diff --git a/component/notification/src/main/java/io/meeds/social/notification/rest/model/EmailDigestChoice.java b/component/notification/src/main/java/io/meeds/social/notification/rest/model/EmailDigestChoice.java
new file mode 100644
index 00000000000..0d1ee396189
--- /dev/null
+++ b/component/notification/src/main/java/io/meeds/social/notification/rest/model/EmailDigestChoice.java
@@ -0,0 +1,71 @@
+/**
+ * This file is part of the Meeds project (https://meeds.io/).
+ *
+ * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ * 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
+ * Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package io.meeds.social.notification.rest.model;
+
+public class EmailDigestChoice {
+ String channelId;
+
+ String pluginId;
+
+ String value;
+
+ boolean channelActive;
+
+ public EmailDigestChoice(String channelId,
+ String pluginId,
+ String value,
+ boolean channelActive) {
+ this.channelId = channelId;
+ this.pluginId = pluginId;
+ this.value = value;
+ this.channelActive = channelActive;
+ }
+
+ public String getChannelId() {
+ return channelId;
+ }
+
+ public void setChannelId(String channelId) {
+ this.channelId = channelId;
+ }
+
+ public String getPluginId() {
+ return pluginId;
+ }
+
+ public void setPluginId(String pluginId) {
+ this.pluginId = pluginId;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public boolean isChannelActive() {
+ return channelActive;
+ }
+
+ public void setChannelActive(boolean channelActive) {
+ this.channelActive = channelActive;
+ }
+
+}
diff --git a/component/notification/src/main/java/io/meeds/social/notification/rest/model/UserNotificationSettings.java b/component/notification/src/main/java/io/meeds/social/notification/rest/model/UserNotificationSettings.java
new file mode 100644
index 00000000000..c7ca6ed6c18
--- /dev/null
+++ b/component/notification/src/main/java/io/meeds/social/notification/rest/model/UserNotificationSettings.java
@@ -0,0 +1,194 @@
+/**
+ * This file is part of the Meeds project (https://meeds.io/).
+ *
+ * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ * 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
+ * Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package io.meeds.social.notification.rest.model;
+
+import java.util.List;
+import java.util.Map;
+
+import org.exoplatform.commons.api.notification.model.GroupProvider;
+import org.exoplatform.commons.api.notification.model.UserSetting;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class UserNotificationSettings {
+ private List groups;
+
+ private Map groupsLabels;
+
+ private Map pluginLabels;
+
+ private Map channelLabels;
+
+ private Map channelDescriptions;
+
+ private Map digestLabels;
+
+ private Map digestDescriptions;
+
+ private boolean hasActivePlugin = false;
+
+ private List emailDigestChoices = null;
+
+ private List channelCheckBoxList = null;
+
+ private Map channelStatus;
+
+ private List channels;
+
+ private String emailChannel = UserSetting.EMAIL_CHANNEL;
+
+ @Getter
+ @Setter
+ private String senderEmail;
+
+ @Getter
+ @Setter
+ private String senderName;
+
+ public UserNotificationSettings(List groups,
+ Map groupsLabels,
+ Map pluginLabels,
+ Map channelLabels,
+ Map channelDescriptions,
+ Map digestLabels,
+ Map digestDescriptions,
+ boolean hasActivePlugin,
+ List emailDigestChoices,
+ List channelCheckBoxList,
+ Map channelStatus,
+ List channels) {
+ this.groups = groups;
+ this.groupsLabels = groupsLabels;
+ this.pluginLabels = pluginLabels;
+ this.channelLabels = channelLabels;
+ this.channelDescriptions = channelDescriptions;
+ this.digestLabels = digestLabels;
+ this.digestDescriptions = digestDescriptions;
+ this.hasActivePlugin = hasActivePlugin;
+ this.emailDigestChoices = emailDigestChoices;
+ this.channelCheckBoxList = channelCheckBoxList;
+ this.channelStatus = channelStatus;
+ this.channels = channels;
+ }
+
+ public List getGroups() {
+ return groups;
+ }
+
+ public void setGroups(List groups) {
+ this.groups = groups;
+ }
+
+ public Map getChannelLabels() {
+ return channelLabels;
+ }
+
+ public void setChannelLabels(Map channelLabels) {
+ this.channelLabels = channelLabels;
+ }
+
+ public Map getChannelDescriptions() {
+ return channelDescriptions;
+ }
+
+ public void setChannelDescriptions(Map channelDescriptions) {
+ this.channelDescriptions = channelDescriptions;
+ }
+
+ public Map getDigestLabels() {
+ return digestLabels;
+ }
+
+ public void setDigestLabels(Map digestLabels) {
+ this.digestLabels = digestLabels;
+ }
+
+ public Map getDigestDescriptions() {
+ return digestDescriptions;
+ }
+
+ public void setDigestDescriptions(Map digestDescriptions) {
+ this.digestDescriptions = digestDescriptions;
+ }
+
+ public boolean isHasActivePlugin() {
+ return hasActivePlugin;
+ }
+
+ public void setHasActivePlugin(boolean hasActivePlugin) {
+ this.hasActivePlugin = hasActivePlugin;
+ }
+
+ public List getEmailDigestChoices() {
+ return emailDigestChoices;
+ }
+
+ public void setEmailDigestChoices(List emailDigestChoices) {
+ this.emailDigestChoices = emailDigestChoices;
+ }
+
+ public Map getGroupsLabels() {
+ return groupsLabels;
+ }
+
+ public void setGroupsLabels(Map groupsLabels) {
+ this.groupsLabels = groupsLabels;
+ }
+
+ public Map getPluginLabels() {
+ return pluginLabels;
+ }
+
+ public void setPluginLabels(Map pluginLabels) {
+ this.pluginLabels = pluginLabels;
+ }
+
+ public List getChannelCheckBoxList() {
+ return channelCheckBoxList;
+ }
+
+ public void setChannelCheckBoxList(List channelCheckBoxList) {
+ this.channelCheckBoxList = channelCheckBoxList;
+ }
+
+ public Map getChannelStatus() {
+ return channelStatus;
+ }
+
+ public void setChannelStatus(Map channelStatus) {
+ this.channelStatus = channelStatus;
+ }
+
+ public List getChannels() {
+ return channels;
+ }
+
+ public void setChannels(List channels) {
+ this.channels = channels;
+ }
+
+ public String getEmailChannel() {
+ return emailChannel;
+ }
+
+ public void setEmailChannel(String emailChannel) {
+ this.emailChannel = emailChannel;
+ }
+
+}
diff --git a/component/notification/src/main/java/org/exoplatform/social/notification/plugin/NewUserPlugin.java b/component/notification/src/main/java/org/exoplatform/social/notification/plugin/NewUserPlugin.java
index 40cd8e92b48..5f6b8b61569 100644
--- a/component/notification/src/main/java/org/exoplatform/social/notification/plugin/NewUserPlugin.java
+++ b/component/notification/src/main/java/org/exoplatform/social/notification/plugin/NewUserPlugin.java
@@ -19,8 +19,6 @@
import org.exoplatform.commons.api.notification.NotificationContext;
import org.exoplatform.commons.api.notification.model.NotificationInfo;
import org.exoplatform.commons.api.notification.plugin.BaseNotificationPlugin;
-import org.exoplatform.commons.api.notification.service.setting.UserSettingService;
-import org.exoplatform.commons.utils.CommonsUtils;
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.social.core.identity.model.Profile;
@@ -41,11 +39,6 @@ public NotificationInfo makeNotification(NotificationContext ctx) {
Profile profile = ctx.value(SocialNotificationUtils.PROFILE);
String remoteId = profile.getIdentity().getRemoteId();
try {
- UserSettingService userSettingService = CommonsUtils.getService(UserSettingService.class);
- //
- userSettingService.initDefaultSettings(remoteId);
- //
-
return NotificationInfo.instance()
.key(getId())
.with(SocialNotificationUtils.REMOTE_ID.getKey(), remoteId)
@@ -53,6 +46,7 @@ public NotificationInfo makeNotification(NotificationContext ctx) {
.setFrom(remoteId)
.end();
} catch (Exception e) {
+ ctx.setException(e);
return null;
}
}
diff --git a/component/notification/src/test/java/io/meeds/social/notification/rest/NotificationSettingsRestServiceTest.java b/component/notification/src/test/java/io/meeds/social/notification/rest/NotificationSettingsRestServiceTest.java
new file mode 100644
index 00000000000..76fba7611cf
--- /dev/null
+++ b/component/notification/src/test/java/io/meeds/social/notification/rest/NotificationSettingsRestServiceTest.java
@@ -0,0 +1,356 @@
+/**
+ * This file is part of the Meeds project (https://meeds.io/).
+ *
+ * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ * 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
+ * Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package io.meeds.social.notification.rest;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.mockito.ArgumentMatcher;
+
+import org.exoplatform.commons.api.notification.NotificationContext;
+import org.exoplatform.commons.api.notification.channel.AbstractChannel;
+import org.exoplatform.commons.api.notification.channel.ChannelManager;
+import org.exoplatform.commons.api.notification.channel.template.AbstractTemplateBuilder;
+import org.exoplatform.commons.api.notification.channel.template.TemplateProvider;
+import org.exoplatform.commons.api.notification.model.ChannelKey;
+import org.exoplatform.commons.api.notification.model.GroupProvider;
+import org.exoplatform.commons.api.notification.model.PluginInfo;
+import org.exoplatform.commons.api.notification.model.PluginKey;
+import org.exoplatform.commons.api.notification.model.UserSetting;
+import org.exoplatform.commons.api.notification.plugin.config.PluginConfig;
+import io.meeds.social.notification.rest.model.UserNotificationSettings;
+import org.exoplatform.commons.api.notification.service.setting.PluginSettingService;
+import org.exoplatform.commons.api.notification.service.setting.UserSettingService;
+import org.exoplatform.portal.config.UserACL;
+import org.exoplatform.services.rest.impl.ContainerResponse;
+import org.exoplatform.services.rest.impl.EnvironmentContext;
+import org.exoplatform.services.rest.impl.MultivaluedMapImpl;
+import org.exoplatform.services.security.ConversationState;
+import org.exoplatform.services.security.Identity;
+import org.exoplatform.services.test.mock.MockHttpServletRequest;
+import org.exoplatform.settings.jpa.JPAUserSettingServiceImpl;
+import org.exoplatform.social.service.rest.BaseRestServicesTestCase;
+
+public class NotificationSettingsRestServiceTest extends BaseRestServicesTestCase { // NOSONAR
+
+ private static final String USER_1 = "testuser1";
+
+ private static final String USER_2 = "testuser2";
+
+ private static final String CHANNEL_ID = "channelId";
+
+ private static final String GROUP_PROVIDER_ID = "groupId";
+
+ private static final String PLUGIN_ID = "pluginId";
+
+ private static final PluginInfo PLUGIN_PROVIDER = new PluginInfo();
+
+ private static final PluginConfig PLUGIN_CONFIG = new PluginConfig();
+ static {
+ PLUGIN_PROVIDER.setType(PLUGIN_ID);
+ PLUGIN_CONFIG.setPluginId(PLUGIN_ID);
+ PLUGIN_CONFIG.setGroupId(GROUP_PROVIDER_ID);
+ }
+
+ private static final List PLUGINS = Collections.singletonList(PLUGIN_PROVIDER);
+
+ private static final GroupProvider GROUP_PROVIDER = new GroupProvider(GROUP_PROVIDER_ID);
+ static {
+ GROUP_PROVIDER.setPluginInfos(PLUGINS);
+ }
+
+ private static final List GROUPS = Collections.singletonList(GROUP_PROVIDER);
+
+ private static final List EMPTY_GROUP_PROVIDER =
+ Collections.singletonList(new GroupProvider(GROUP_PROVIDER_ID));
+
+ private static final List CHANNELS = Collections.singletonList(newChannel());
+
+ private ChannelManager channelManager;
+
+ private UserSettingService userSettingService;
+
+ private UserACL userACL;
+
+ private PluginSettingService pluginSettingService;
+
+ @Override
+ protected Class> getComponentClass() {
+ return NotificationSettingsRestService.class;
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ pluginSettingService = mock(PluginSettingService.class);
+ channelManager = mock(ChannelManager.class);
+ userSettingService = mock(UserSettingService.class);
+ userACL = mock(UserACL.class);
+
+ when(userACL.isSuperUser()).thenReturn(false);
+ when(userACL.getAdminGroups()).thenReturn("admins");
+
+ when(channelManager.getChannels()).thenReturn(CHANNELS);
+
+ when(pluginSettingService.getGroupPlugins()).thenReturn(GROUPS);
+ when(pluginSettingService.getPluginConfig(eq(PLUGIN_ID))).thenReturn(PLUGIN_CONFIG);
+ when(pluginSettingService.isChannelActive(CHANNEL_ID)).thenReturn(true);
+
+ UserSetting userSetting = new UserSetting();
+ userSetting.setChannelActive(CHANNEL_ID);
+ userSetting.setEnabled(true);
+ userSetting.setUserId(USER_1);
+ userSetting.setChannelPlugins(CHANNEL_ID, Collections.singletonList(PLUGIN_ID));
+ when(userSettingService.get(eq(USER_1))).thenReturn(userSetting);
+
+ getContainer().unregisterComponent(PluginSettingService.class);
+ getContainer().unregisterComponent(ChannelManager.class);
+ getContainer().unregisterComponent(UserSettingService.class);
+ getContainer().unregisterComponent(JPAUserSettingServiceImpl.class);
+ getContainer().unregisterComponent(UserACL.class);
+
+ getContainer().registerComponentInstance(PluginSettingService.class.getName(), pluginSettingService);
+ getContainer().registerComponentInstance(ChannelManager.class.getName(), channelManager);
+ getContainer().registerComponentInstance(UserSettingService.class.getName(), userSettingService);
+ getContainer().registerComponentInstance(UserACL.class.getName(), userACL);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ getContainer().unregisterComponent(PluginSettingService.class.getName());
+ getContainer().unregisterComponent(ChannelManager.class.getName());
+ getContainer().unregisterComponent(UserSettingService.class.getName());
+ getContainer().unregisterComponent(UserACL.class.getName());
+ super.tearDown();
+ }
+
+ public void testUnauthorizedNotSameUserGetSettings() throws Exception {
+ // Given
+ String path = getPath(USER_1, "");
+ MockHttpServletRequest httpRequest = new MockHttpServletRequest(path,
+ null,
+ 0,
+ "GET",
+ null);
+
+ EnvironmentContext envctx = new EnvironmentContext();
+ envctx.put(HttpServletRequest.class, httpRequest);
+
+ startSessionAs(USER_2);
+
+ // When
+ ContainerResponse resp = launcher.service("GET",
+ path,
+ "",
+ null,
+ null,
+ envctx);
+
+ // Then
+ assertEquals(String.valueOf(resp.getEntity()), 401, resp.getStatus()); // NOSONAR
+ }
+
+ public void testAdminGetSettings() throws Exception {
+ // Given
+ String path = getPath(USER_1, "");
+ MockHttpServletRequest httpRequest = new MockHttpServletRequest(path,
+ null,
+ 0,
+ "GET",
+ null);
+
+ EnvironmentContext envctx = new EnvironmentContext();
+ envctx.put(HttpServletRequest.class, httpRequest);
+
+ startSessionAs(USER_2);
+ when(userACL.isUserInGroup(eq("admins"))).thenReturn(true);
+
+ // When
+ ContainerResponse resp = launcher.service("GET",
+ path,
+ "",
+ null,
+ null,
+ envctx);
+
+ // Then
+ assertEquals(String.valueOf(resp.getEntity()), 200, resp.getStatus());
+ UserNotificationSettings notificationSettings = (UserNotificationSettings) resp.getEntity();
+ assertNotNull(notificationSettings);
+ }
+
+ public void testGetSettingsSameUser() throws Exception {
+ // Given
+ String path = getPath(USER_1, "");
+ MockHttpServletRequest httpRequest = new MockHttpServletRequest(path,
+ null,
+ 0,
+ "GET",
+ null);
+
+ EnvironmentContext envctx = new EnvironmentContext();
+ envctx.put(HttpServletRequest.class, httpRequest);
+
+ startSessionAs(USER_1);
+
+ // When
+ ContainerResponse resp = launcher.service("GET",
+ path,
+ "",
+ null,
+ null,
+ envctx);
+
+ // Then
+ assertEquals(String.valueOf(resp.getEntity()), 200, resp.getStatus());
+ UserNotificationSettings notificationSettings = (UserNotificationSettings) resp.getEntity();
+ assertNotNull(notificationSettings);
+ assertNotNull(notificationSettings.getChannels());
+ assertEquals(1, notificationSettings.getChannels().size());
+ assertNotNull(notificationSettings.getGroups());
+ assertEquals(1, notificationSettings.getGroups().size());
+ assertNotNull(notificationSettings.getGroups().get(0));
+ assertEquals(GROUP_PROVIDER_ID, notificationSettings.getGroups().get(0).getGroupId());
+ assertNotNull(notificationSettings.getGroups().get(0).getGroupId());
+ assertNotNull(notificationSettings.getChannelStatus());
+ assertEquals(1, notificationSettings.getChannelStatus().size());
+ assertTrue(notificationSettings.getChannelStatus().get(CHANNEL_ID));
+ }
+
+ public void testGetSettingsSameUserEmptyPlugins() throws Exception {
+ // Given
+ when(pluginSettingService.getGroupPlugins()).thenReturn(EMPTY_GROUP_PROVIDER);
+
+ // Ensure no error is raised
+ testGetSettingsSameUser();
+ }
+
+ public void testSaveDisableChannel() throws Exception {
+ // Given
+ String path = getPath(USER_1, "channel/" + CHANNEL_ID);
+ MockHttpServletRequest httpRequest = new MockHttpServletRequest(path,
+ null,
+ 0,
+ "PATCH",
+ null);
+
+ EnvironmentContext envctx = new EnvironmentContext();
+ envctx.put(HttpServletRequest.class, httpRequest);
+
+ startSessionAs(USER_1);
+
+ // When
+ ContainerResponse resp = launcher.service("PATCH",
+ path,
+ "",
+ getFormHeaders(),
+ ("enable=false").getBytes(),
+ envctx);
+
+ // Then
+ assertEquals(String.valueOf(resp.getEntity()), 204, resp.getStatus());
+ verify(userSettingService, times(1)).save(any());
+ }
+
+ public void testSaveSettings() throws Exception { // NOSONAR
+ // Given
+ String path = getPath(USER_1, "plugin/" + PLUGIN_ID);
+ MockHttpServletRequest httpRequest = new MockHttpServletRequest(path,
+ null,
+ 0,
+ "PATCH",
+ null);
+
+ EnvironmentContext envctx = new EnvironmentContext();
+ envctx.put(HttpServletRequest.class, httpRequest);
+
+ startSessionAs(USER_1);
+
+ // When
+ ContainerResponse resp = launcher.service("PATCH",
+ path,
+ "",
+ getFormHeaders(),
+ ("channels=" + CHANNEL_ID + "=true&digest=Weekly").getBytes(),
+ envctx);
+
+ // Then
+ assertEquals(String.valueOf(resp.getEntity()), 204, resp.getStatus());
+ verify(userSettingService, times(1)).save(argThat(userSetting ->
+ userSetting.isActive(CHANNEL_ID, PLUGIN_ID) && userSetting.isChannelGloballyActive(CHANNEL_ID)
+ && userSetting.isInWeekly(PLUGIN_ID)));
+ }
+
+ private MultivaluedMap getFormHeaders() {
+ MultivaluedMap headers = new MultivaluedMapImpl();
+ headers.putSingle("Content-Type", "application/x-www-form-urlencoded");
+ return headers;
+ }
+
+ private String getPath(String username, String prefix) {
+ return "/notifications/settings/" + username + "/" + prefix;
+ }
+
+ private void startSessionAs(String username) {
+ Identity identity = new Identity(username);
+ ConversationState state = new ConversationState(identity);
+ ConversationState.setCurrent(state);
+ }
+
+ private static AbstractChannel newChannel() {
+ return new AbstractChannel() {
+
+ @Override
+ public void registerTemplateProvider(TemplateProvider provider) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected AbstractTemplateBuilder getTemplateBuilderInChannel(PluginKey key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ChannelKey getKey() {
+ return ChannelKey.key(CHANNEL_ID);
+ }
+
+ @Override
+ public String getId() {
+ return CHANNEL_ID;
+ }
+
+ @Override
+ public void dispatch(NotificationContext ctx, String userId) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+}
diff --git a/component/notification/src/test/java/io/meeds/social/notification/rest/WebNotificationRestServiceTest.java b/component/notification/src/test/java/io/meeds/social/notification/rest/WebNotificationRestServiceTest.java
new file mode 100644
index 00000000000..6e97fdb1842
--- /dev/null
+++ b/component/notification/src/test/java/io/meeds/social/notification/rest/WebNotificationRestServiceTest.java
@@ -0,0 +1,110 @@
+/**
+ * This file is part of the Meeds project (https://meeds.io/).
+ *
+ * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ * 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
+ * Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package io.meeds.social.notification.rest;
+
+import org.exoplatform.commons.api.notification.model.NotificationInfo;
+import org.exoplatform.commons.api.notification.service.WebNotificationService;
+import org.exoplatform.services.security.ConversationState;
+import org.exoplatform.services.security.Identity;
+import org.exoplatform.social.service.rest.BaseRestServicesTestCase;
+
+import javax.ws.rs.core.Response;
+
+import org.mockito.Mock;
+import static org.mockito.Mockito.*;
+
+public class WebNotificationRestServiceTest extends BaseRestServicesTestCase { // NOSONAR
+
+ @Mock
+ WebNotificationService webNotificationService;
+
+ @Override
+ protected Class> getComponentClass() {
+ return WebNotificationRestService.class;
+ }
+
+ public void testUnauthorizedMarkAsRead() {
+ startSessionAs("john");
+
+ webNotificationService = mock(WebNotificationService.class);
+ NotificationInfo notificationInfo = new NotificationInfo();
+ notificationInfo.setTo("mary");
+ when(webNotificationService.getNotificationInfo(anyString())).thenReturn(notificationInfo);
+
+ WebNotificationRestService webNotificationRestService = new WebNotificationRestService(webNotificationService);
+ Response response = webNotificationRestService.updateNotifications("markAsRead", "1");
+
+ assertEquals(401, response.getStatus()); // NOSONAR
+ }
+
+ public void testAuthorizedMarkAsRead() {
+ startSessionAs("john");
+
+ webNotificationService = mock(WebNotificationService.class);
+
+ NotificationInfo notificationInfo = new NotificationInfo();
+ notificationInfo.setTo("john");
+
+ when(webNotificationService.getNotificationInfo(anyString())).thenReturn(notificationInfo);
+
+ WebNotificationRestService webNotificationRestService = new WebNotificationRestService(webNotificationService);
+ Response response = webNotificationRestService.updateNotifications("markAsRead", "1");
+
+ assertEquals(204, response.getStatus());
+
+ }
+
+ public void testUnauthorizedHide() {
+ startSessionAs("john");
+
+ webNotificationService = mock(WebNotificationService.class);
+
+ NotificationInfo notificationInfo = new NotificationInfo();
+ notificationInfo.setTo("mary");
+ when(webNotificationService.getNotificationInfo(anyString())).thenReturn(notificationInfo);
+
+ WebNotificationRestService webNotificationRestService = new WebNotificationRestService(webNotificationService);
+ Response response = webNotificationRestService.updateNotifications("hide", "1");
+
+ assertEquals(response.getStatus(), 401);
+
+ }
+
+ public void testAuthorizedHide() {
+ startSessionAs("john");
+
+ webNotificationService = mock(WebNotificationService.class);
+
+ NotificationInfo notificationInfo = new NotificationInfo();
+ notificationInfo.setTo("john");
+ when(webNotificationService.getNotificationInfo(anyString())).thenReturn(notificationInfo);
+
+ WebNotificationRestService webNotificationRestService = new WebNotificationRestService(webNotificationService);
+ Response response = webNotificationRestService.updateNotifications("hide", "1");
+
+ assertEquals(204, response.getStatus());
+
+ }
+
+ private void startSessionAs(String username) {
+ Identity identity = new Identity(username);
+ ConversationState state = new ConversationState(identity);
+ ConversationState.setCurrent(state);
+ }
+
+}
diff --git a/component/notification/src/test/java/org/exoplatform/social/notification/InitContainerTestSuiteRest.java b/component/notification/src/test/java/org/exoplatform/social/notification/InitContainerTestSuiteRest.java
new file mode 100644
index 00000000000..14d3d46c835
--- /dev/null
+++ b/component/notification/src/test/java/org/exoplatform/social/notification/InitContainerTestSuiteRest.java
@@ -0,0 +1,50 @@
+/**
+ * This file is part of the Meeds project (https://meeds.io/).
+ *
+ * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ * 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
+ * Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.exoplatform.social.notification;
+
+import org.exoplatform.commons.testing.BaseExoContainerTestSuite;
+import org.exoplatform.commons.testing.ConfigTestCase;
+
+import io.meeds.social.notification.rest.NotificationSettingsRestServiceTest;
+import io.meeds.social.notification.rest.WebNotificationRestServiceTest;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses({
+ WebNotificationRestServiceTest.class,
+ NotificationSettingsRestServiceTest.class,
+})
+@ConfigTestCase(AbstractCoreTest.class)
+public class InitContainerTestSuiteRest extends BaseExoContainerTestSuite {
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ initConfiguration(InitContainerTestSuiteRest.class);
+ beforeSetup();
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ afterTearDown();
+ }
+}
diff --git a/extension/war/src/main/webapp/WEB-INF/conf/portal/group/platform/administrators/pages.xml b/extension/war/src/main/webapp/WEB-INF/conf/portal/group/platform/administrators/pages.xml
index df223ceff1d..1db9f606baf 100644
--- a/extension/war/src/main/webapp/WEB-INF/conf/portal/group/platform/administrators/pages.xml
+++ b/extension/war/src/main/webapp/WEB-INF/conf/portal/group/platform/administrators/pages.xml
@@ -115,6 +115,7 @@
+
notificationNotifications Administration
@@ -124,9 +125,8 @@
*:/platform/administrators
- commons-extension
- NotificationsAdminJuzuPortlet
-
+ social-portlet
+ NotificationAdministrationNotifications Administrationmanager:/platform/administrators
@@ -136,4 +136,5 @@
+
diff --git a/extension/war/src/main/webapp/WEB-INF/conf/social-extension/social/notification-configuration.xml b/extension/war/src/main/webapp/WEB-INF/conf/social-extension/social/notification-configuration.xml
index 044f17c080d..7b3abe9d53f 100644
--- a/extension/war/src/main/webapp/WEB-INF/conf/social-extension/social/notification-configuration.xml
+++ b/extension/war/src/main/webapp/WEB-INF/conf/social-extension/social/notification-configuration.xml
@@ -9,6 +9,14 @@
org.exoplatform.social.notification.impl.SpaceWebNotificationServiceImpl
+
+ io.meeds.social.notification.rest.NotificationSettingsRestService
+
+
+
+ io.meeds.social.notification.rest.WebNotificationRestService
+
+
org.exoplatform.social.notification.service.SpaceWebNotificationService
diff --git a/translations.properties b/translations.properties
index a8ab1b409e9..bd028c83d10 100644
--- a/translations.properties
+++ b/translations.properties
@@ -52,6 +52,8 @@ webapp/SpacesOverview.properties=webapp/portlet/src/main/resources/locale/portle
webapp/SuggestionsPortlet.properties=webapp/portlet/src/main/resources/locale/portlet/social/SuggestionsPortlet_en.properties
webapp/UserSettings.properties=webapp/portlet/src/main/resources/locale/portlet/social/UserSettings_en.properties
webapp/Login.properties=webapp/portlet/src/main/resources/locale/portlet/Login_en.properties
+webapp/UserNotificationPortlet.properties=webapp/portlet/src/main/resources/locale/portlet/UserNotificationPortlet_en.properties
+webapp/NotificationAdministration.properties=webapp/portlet/src/main/resources/locale/portlet/NotificationAdministration_en.properties
# navigation
navigation/users.properties=extension/war/src/main/resources/locale/navigation/group/platform/users_en.properties
diff --git a/webapp/portlet/src/main/resources/locale/portlet/NotificationAdministration_en.properties b/webapp/portlet/src/main/resources/locale/portlet/NotificationAdministration_en.properties
new file mode 100644
index 00000000000..25e5a7d78dc
--- /dev/null
+++ b/webapp/portlet/src/main/resources/locale/portlet/NotificationAdministration_en.properties
@@ -0,0 +1,33 @@
+NotificationAdmin.title=Manage notifications settings
+NotificationAdmin.sender.title=Notification Sender
+NotificationAdmin.sender.subtitle=Set name and email of the notification sender
+NotificationAdmin.sender.drawer.message=Notification will be sent using the characteristics below
+NotificationAdmin.sender.drawer.name=Name
+NotificationAdmin.sender.drawer.address=Address
+NotificationAdmin.allowedNotifications.title=Allowed notification channels
+NotificationAdmin.allowedNotifications.subtitle=Precise notification channels that users would be allowed to use.
+NotificationAdmin.MAIL_CHANNEL.title=Enable email notifications for all
+NotificationAdmin.WEB_CHANNEL.title=Enable onsite notifications for all
+NotificationAdmin.SPACE_WEB_CHANNEL.title=Enable unread activities notifications for all
+NotificationAdmin.PUSH_CHANNEL.title=Enable push notifications for all
+NotificationAdmin.MAIL_CHANNEL.name=Email notifications
+NotificationAdmin.WEB_CHANNEL.name=On-site notifications
+NotificationAdmin.SPACE_WEB_CHANNEL.name=Unread activities notifications
+NotificationAdmin.PUSH_CHANNEL.name=Mobile push notifications
+NotificationAdmin.allowedNotificationChannels.title=Allowed notification channels per type
+NotificationAdmin.invalidSenderEmail=Invalid email
+NotificationAdmin.invalidSenderName=Invalid name. Please use alphanumeric ' - _ characters only
+NotificationAdmin.NewUserPlugin=A new user joins the platform
+NotificationAdmin.RelationshipReceivedRequestPlugin=A connection request is sent
+NotificationAdmin.SpaceInvitationPlugin=An invitation to join a space is done
+NotificationAdmin.RequestJoinSpacePlugin=A request to join a space is done
+NotificationAdmin.ActivityMentionPlugin=User is mentioned in a message
+NotificationAdmin.SharedActivitySpaceStreamPlugin=An activity is shared to a space
+NotificationAdmin.PostActivityPlugin=An activity is posted on a personal stream
+NotificationAdmin.PostActivitySpaceStreamPlugin=An activity is posted on a space stream
+NotificationAdmin.EditActivityPlugin=An activity is edited
+NotificationAdmin.ActivityCommentPlugin=An activity is commented
+NotificationAdmin.EditCommentPlugin=A comment is edited
+NotificationAdmin.ActivityReplyToCommentPlugin=A reply to a comment is added
+NotificationAdmin.LikePlugin=A like on activity is added
+NotificationAdmin.LikeCommentPlugin=A like on comment is added
diff --git a/webapp/portlet/src/main/resources/locale/portlet/NotificationAdministration_fr.properties b/webapp/portlet/src/main/resources/locale/portlet/NotificationAdministration_fr.properties
new file mode 100644
index 00000000000..a1bab298334
--- /dev/null
+++ b/webapp/portlet/src/main/resources/locale/portlet/NotificationAdministration_fr.properties
@@ -0,0 +1,29 @@
+NotificationAdmin.title=Manage notifications settings
+NotificationAdmin.sender.title=Notification Sender
+NotificationAdmin.sender.subtitle=Set name and email of the notification sender
+NotificationAdmin.sender.drawer.message=Notification will be sent using the characteristics below
+NotificationAdmin.sender.drawer.name=Name
+NotificationAdmin.sender.drawer.address=Address
+NotificationAdmin.allowedNotifications.title=Allowed notification channels
+NotificationAdmin.allowedNotifications.subtitle=Precise notification channels that users would be allowed to use.
+NotificationAdmin.MAIL_CHANNEL.title=Enable email notifications for all
+NotificationAdmin.WEB_CHANNEL.title=Enable onsite notifications for all
+NotificationAdmin.SPACE_WEB_CHANNEL.title=Enable unread activities notifications for all
+NotificationAdmin.PUSH_CHANNEL.title=Enable push notifications for all
+NotificationAdmin.allowedNotificationChannels.title=Allowed notification channels per type
+NotificationAdmin.invalidSenderEmail=Invalid email
+NotificationAdmin.invalidSenderName=Invalid name. Please use alphanumeric ' - _ characters only
+NotificationAdmin.NewUserPlugin=Un nouvel utilisateur rejoint la plateforme
+NotificationAdmin.RelationshipReceivedRequestPlugin=Une demande de connexion est envoy\u00E9e
+NotificationAdmin.SpaceInvitationPlugin=Une invitation \u00E0 rejoindre un espace est envoy\u00E9e
+NotificationAdmin.RequestJoinSpacePlugin=Une demande pour rejoindre un espace est transmise
+NotificationAdmin.ActivityMentionPlugin=Un utilisateur est mentionn\u00E9 dans un espace
+NotificationAdmin.SharedActivitySpaceStreamPlugin=Une activit\u00E9 est partag\u00E9e dans un espace
+NotificationAdmin.PostActivityPlugin=Une activit\u00E9 est publi\u00E9e sur le fil personnel
+NotificationAdmin.PostActivitySpaceStreamPlugin=Une activit\u00E9 est publi\u00E9e sur le fil d'un espace
+NotificationAdmin.EditActivityPlugin=Une activit\u00E9 a \u00E9t\u00E9 modifi\u00E9e
+NotificationAdmin.ActivityCommentPlugin=Une activit\u00E9 est comment\u00E9e
+NotificationAdmin.EditCommentPlugin=Un commentaire a \u00E9t\u00E9 modifi\u00E9
+NotificationAdmin.ActivityReplyToCommentPlugin=Une r\u00E9ponse \u00E0 un commentaire est ajout\u00E9e
+NotificationAdmin.LikePlugin=Un j'aime sur une activit\u00E9 est ajout\u00E9
+NotificationAdmin.LikeCommentPlugin=Un j'aime sur un commentaire est ajout\u00E9
diff --git a/webapp/portlet/src/main/resources/locale/portlet/UserNotificationPortlet_en.properties b/webapp/portlet/src/main/resources/locale/portlet/UserNotificationPortlet_en.properties
new file mode 100644
index 00000000000..3782c68f1e2
--- /dev/null
+++ b/webapp/portlet/src/main/resources/locale/portlet/UserNotificationPortlet_en.properties
@@ -0,0 +1,50 @@
+#####################################################################################
+# Notification Settings #
+#####################################################################################
+UINotification.label.NotificationsDescription=We will notify you whenever something involving you happens. The settings below will let you control how you want to be notified.
+UINotification.label.NotificationSettings=Notification Settings
+
+UINotification.label.channel-mail=Notify me by email
+UINotification.description.channel-mail=You will be notified by an email sent to your private mail box
+UINotification.label.channel-web=Notify me on-site
+UINotification.label.channel-space_web=Notify me when unread activities
+UINotification.description.channel-space_web=You will be notified in the left menu and in the stream
+UINotification.description.channel-web=You'll receive a notification directly on the website.
+UINotification.label.channel.default=Notify me by {0}
+UINotification.description.channel.default=You will be notified by {0}
+
+UINotification.label.Empty=No providers
+UINotification.label.NotifyMeWhen=Notify me when
+UINotification.label.HowToGetNotification=How to get notifications
+
+UINotification.label.NoNotifications=No notifications
+UINotification.label.checkbox-mail=Send me an email right away
+UINotification.label.selectBox-mail=Send me a digest email
+UINotification.label.checkbox-web=See on site
+UINotification.label.checkbox.default=See on {0}
+
+UINotification.label.Never=Never
+UINotification.label.Daily=Daily
+UINotification.description.Daily=Daily digest email notification
+UINotification.label.Weekly=Weekly
+UINotification.description.Weekly=Weekly digest email notification
+UINotification.label.Monthly=Monthly
+UINotification.description.Monthly=Monthly digest email notification
+
+UINotification.title.Information=Information
+UINotification.title.Confirmation=Confirmation
+
+UINotification.msg.NoPluginIsActive=All notifications have been disabled by an administrator.
+UINotification.msg.SaveOKSetting=Your changes have been saved.
+UINotification.msg.SaveNOKSetting=Your changes cannot be saved.
+UINotification.msg.ResetSetting=All your notification settings will be reset to default values. Your previous settings will be lost.
+
+UINotification.action.Save=Save
+#
+UINotification.action.Reset=Reset
+UINotification.action.Confirm=Confirm
+UINotification.action.Cancel=Cancel
+UINotification.action.OK=OK
+UINotification.action.Close=Close
+UINotification.action.switch.on=Yes
+UINotification.action.switch.off=No
diff --git a/webapp/portlet/src/main/resources/locale/portlet/UserNotificationPortlet_fr.properties b/webapp/portlet/src/main/resources/locale/portlet/UserNotificationPortlet_fr.properties
new file mode 100644
index 00000000000..a56ee9ed9e9
--- /dev/null
+++ b/webapp/portlet/src/main/resources/locale/portlet/UserNotificationPortlet_fr.properties
@@ -0,0 +1,50 @@
+#####################################################################################
+# Notification Settings #
+#####################################################################################
+UINotification.label.NotificationsDescription=Nous pouvons vous notifier \u00E0 chaque fois qu'il arrive quelque chose vous concernant. Les param\u00E8tres ci-dessous vous permettront de contr\u00F4ler la fa\u00E7on dont vous souhaitez \u00EAtre averti(e).
+UINotification.label.NotificationSettings=Param\u00E8tres de Notification
+
+UINotification.label.channel-mail=E-mail
+UINotification.description.channel-mail=Vous recevrez une notification par email
+UINotification.label.channel-web=Site
+UINotification.label.channel-space_web=Me notifier en cas d'activit\u00E9 non lue
+UINotification.description.channel-space_web=Vous serez notifi\u00E9 dans le menu de gauche et dans le fil d'activit\u00E9s
+UINotification.description.channel-web=Vous recevrez une notification directement sur le site
+UINotification.label.channel.default=Me notifier par {0}
+UINotification.description.channel.default=Vous serez notifi\u00E9 par {0}
+
+UINotification.label.Empty=Aucun fournisseur
+UINotification.label.NotifyMeWhen=Me notifier lorsque...
+UINotification.label.HowToGetNotification=Comment \u00EAtre notifi\u00E9 ?
+
+UINotification.label.NoNotifications=Ne pas me notifier
+UINotification.label.checkbox-mail=Envoyez moi un email imm\u00E9diatement
+UINotification.label.selectBox-mail=Envoyez-moi un email r\u00E9sum\u00E9
+UINotification.label.checkbox-web=Affichez la notification sur le site
+UINotification.label.checkbox.default=Voir sur {0}
+
+UINotification.label.Never=Jamais
+UINotification.label.Daily=Quotidien
+UINotification.description.Daily=R\u00E9capitulatif quotidien des notifications par email
+UINotification.label.Weekly=Hebdomadaire
+UINotification.description.Weekly=R\u00E9capitulatif hebdomadaire des notifications par email
+UINotification.label.Monthly=Mensuel
+UINotification.description.Monthly=R\u00E9capitulatif mensuel des notifications par email
+
+UINotification.title.Information=Information:
+UINotification.title.Confirmation=Confirmation
+
+UINotification.msg.NoPluginIsActive=Toutes les notifications ont \u00E9t\u00E9 d\u00E9sactiv\u00E9es par un administrateur.
+UINotification.msg.SaveOKSetting=Vos modifications ont \u00E9t\u00E9 enregistr\u00E9es.
+UINotification.msg.SaveNOKSetting=Impossible d'enregistrer vos modifications.
+UINotification.msg.ResetSetting=Tous vos param\u00E8tres de notification seront r\u00E9initialis\u00E9s aux valeurs par d\u00E9faut. Vos param\u00E8tres actuels seront perdus.
+
+UINotification.action.Save=Enregistrer
+#
+UINotification.action.Reset=R\u00E9initialiser
+UINotification.action.Confirm=Confirmer
+UINotification.action.Cancel=Annuler
+UINotification.action.OK=Valider
+UINotification.action.Close=Fermer
+UINotification.action.switch.on=Oui
+UINotification.action.switch.off=Non
diff --git a/webapp/portlet/src/main/webapp/WEB-INF/gatein-resources.xml b/webapp/portlet/src/main/webapp/WEB-INF/gatein-resources.xml
index da0930b4fff..66632290a48 100644
--- a/webapp/portlet/src/main/webapp/WEB-INF/gatein-resources.xml
+++ b/webapp/portlet/src/main/webapp/WEB-INF/gatein-resources.xml
@@ -249,7 +249,7 @@
2
-
+
social-portletIntranetNotificationsPortlet
@@ -265,15 +265,7 @@
/skin/css/portlet/TopBarNotification/Style.css1
-
-
- commons-extension
- NotificationsAdminJuzuPortlet
- Enterprise
- /skin/css/portlet/uiNotificationsAdmin/Style.css
- 1
-
-
+
@@ -1044,6 +1036,31 @@
+
+ NotificationAdministration
+
+
+
+ commonVueComponents
+
+
+ vue
+
+
+ vuetify
+
+
+ eXoVueI18n
+
+
+ extensionRegistry
+
+
+
+
UserSettingSecurity
diff --git a/webapp/portlet/src/main/webapp/WEB-INF/jsp/profileSettings.jsp b/webapp/portlet/src/main/webapp/WEB-INF/jsp/portlet/profileSettings.jsp
similarity index 100%
rename from webapp/portlet/src/main/webapp/WEB-INF/jsp/profileSettings.jsp
rename to webapp/portlet/src/main/webapp/WEB-INF/jsp/portlet/profileSettings.jsp
diff --git a/webapp/portlet/src/main/webapp/WEB-INF/jsp/portlet/userSettingNotifications.jsp b/webapp/portlet/src/main/webapp/WEB-INF/jsp/portlet/userSettingNotifications.jsp
deleted file mode 100644
index 3615f3c58e6..00000000000
--- a/webapp/portlet/src/main/webapp/WEB-INF/jsp/portlet/userSettingNotifications.jsp
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/webapp/portlet/src/main/webapp/WEB-INF/portlet.xml b/webapp/portlet/src/main/webapp/WEB-INF/portlet.xml
index ebeab9ee926..db579f5c845 100644
--- a/webapp/portlet/src/main/webapp/WEB-INF/portlet.xml
+++ b/webapp/portlet/src/main/webapp/WEB-INF/portlet.xml
@@ -162,7 +162,6 @@
Who is on lineWho is on line
- Juzu social intranet
@@ -313,11 +312,15 @@
org.exoplatform.commons.api.portlet.GenericDispatchedViewPortletportlet-view-dispatched-file-path
- /WEB-INF/jsp/portlet/userSettingNotifications.jsp
+ /html/userSettingNotifications.html
+ -1
+ PUBLICtext/html
+ en
+ locale.portlet.UserNotificationPortletUser Setting notifications
@@ -781,7 +784,7 @@
org.exoplatform.commons.api.portlet.GenericDispatchedViewPortletportlet-view-dispatched-file-path
- /WEB-INF/jsp/profileSettings.jsp
+ /WEB-INF/jsp/portlet/profileSettings.jsptext/html
@@ -841,4 +844,23 @@
+
+ NotificationAdministration
+ org.exoplatform.commons.api.portlet.GenericDispatchedViewPortlet
+
+ portlet-view-dispatched-file-path
+ /html/notificationAdministration.html
+
+ -1
+ PUBLIC
+
+ text/html
+
+ en
+ locale.portlet.NotificationAdministration
+
+ Notification Administration
+
+
+
diff --git a/webapp/portlet/src/main/webapp/html/notificationAdministration.html b/webapp/portlet/src/main/webapp/html/notificationAdministration.html
new file mode 100644
index 00000000000..a537baed890
--- /dev/null
+++ b/webapp/portlet/src/main/webapp/html/notificationAdministration.html
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/webapp/portlet/src/main/webapp/html/userSettingNotifications.html b/webapp/portlet/src/main/webapp/html/userSettingNotifications.html
new file mode 100644
index 00000000000..953939eddd2
--- /dev/null
+++ b/webapp/portlet/src/main/webapp/html/userSettingNotifications.html
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/webapp/portlet/src/main/webapp/skin/less/portlet/uiNotificationsAdmin/Style.less b/webapp/portlet/src/main/webapp/skin/less/portlet/uiNotificationsAdmin/Style.less
deleted file mode 100644
index c6b9ef51906..00000000000
--- a/webapp/portlet/src/main/webapp/skin/less/portlet/uiNotificationsAdmin/Style.less
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
-
- This file is part of the Meeds project (https://meeds.io/).
-
- Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 3 of the License, or (at your option) any later version.
- 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
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License
- along with this program; if not, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-*/
-
-.uiNotificationAdmin {
-
- @import "../../deprecated/Style.less";
-
- @import "../../variables.less";
- @import "../../mixins.less";
-
- .senderForm {
- label {
- line-height: 27px;
- margin-bottom: 0;
- margin-right: 6px ~'; /** orientation=lt */ ';
- margin-left: 6px ~'; /** orientation=rt */ ';
- }
- }
- .uiGrid.table {
- thead {
- th:last-child {
- width: 40%;
- }
- }
- tr.disable {
- color: @tableBackgroundAccent;
- }
- tr {
- td {
- &:first-child{
- >label{
- padding-left: 20px;
- }
- }
- }
- td .view .edit-mode,
- td .view .save-setting {
- display: none;
- }
- td .edit .view-mode,
- td .edit .edit-setting {
- display: none;
- }
-
- }
- }
-
- .view-mode, .edit-mode {
- > div {
- padding: 2px 0;
- display: flex;
- }
- }
- .view-mode {
-
- [class^="uiIconPLF"] {
- display: flex;
- min-width: 20px;
- margin-right: 7px ~'; /** orientation=lt */ ';
- margin-left: 7px ~'; /** orientation=rt */ ';
-
- &:before {
- margin: 0 auto;
- }
- }
- > span {
- color: @baseColorLight;
- }
- }
- .edit-mode {
- .uiCheckbox {
- margin-left: 0 ~'; /** orientation=lt */ ';
- margin-right: 0 ~'; /** orientation=rt */ ';
- .checkbox + span {
- padding-left: 6px ~'; /** orientation=lt */ ';
- padding-right: 6px ~'; /** orientation=rt */ ';
- }
- }
- }
- .plugin-container {
- position: relative;
- .right-view {
- position: absolute;
- top: 50%;
- right: 0 ~'; /** orientation=lt */ ';
- left: 0 ~'; /** orientation=rt */ ';
- .translate(0, -50%);
- }
- }
- .inputContainer {
- margin: 0 auto;
- }
-}
-
-
-@media (max-width: 480px) {
- .uiNotificationAdmin {
- padding: 15px 10px;
- }
-}
\ No newline at end of file
diff --git a/webapp/portlet/src/main/webapp/vue-apps/notification-administration/components/NotificationAdministration.vue b/webapp/portlet/src/main/webapp/vue-apps/notification-administration/components/NotificationAdministration.vue
new file mode 100644
index 00000000000..64d66b03d78
--- /dev/null
+++ b/webapp/portlet/src/main/webapp/vue-apps/notification-administration/components/NotificationAdministration.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
diff --git a/webapp/portlet/src/main/webapp/vue-apps/notification-administration/initComponents.js b/webapp/portlet/src/main/webapp/vue-apps/notification-administration/initComponents.js
new file mode 100644
index 00000000000..038f3b6c2a3
--- /dev/null
+++ b/webapp/portlet/src/main/webapp/vue-apps/notification-administration/initComponents.js
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the Meeds project (https://meeds.io/).
+ *
+ * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import NotificationAdministration from './components/NotificationAdministration.vue';
+
+import NotificationPlugins from './components/NotificationPlugins.vue';
+import NotificationChannels from './components/NotificationChannels.vue';
+import NotificationContact from './components/NotificationContact.vue';
+
+import NotificationPluginGroup from './components/plugin/NotificationPluginGroup.vue';
+import NotificationPlugin from './components/plugin/NotificationPlugin.vue';
+import NotificationPluginDrawer from './components/plugin/NotificationPluginDrawer.vue';
+
+import NotificationContactDrawer from './components/contact/NotificationContactDrawer.vue';
+
+const components = {
+ 'notification-administration': NotificationAdministration,
+ 'notification-administration-plugins': NotificationPlugins,
+ 'notification-administration-channels': NotificationChannels,
+ 'notification-administration-contact': NotificationContact,
+ 'notification-administration-plugin': NotificationPlugin,
+ 'notification-administration-plugin-group': NotificationPluginGroup,
+ 'notification-administration-plugin-drawer': NotificationPluginDrawer,
+ 'notification-administration-contact-drawer': NotificationContactDrawer,
+};
+
+for (const key in components) {
+ Vue.component(key, components[key]);
+}
diff --git a/webapp/portlet/src/main/webapp/vue-apps/notification-administration/js/NotificationAdministration.js b/webapp/portlet/src/main/webapp/vue-apps/notification-administration/js/NotificationAdministration.js
new file mode 100644
index 00000000000..359f1401795
--- /dev/null
+++ b/webapp/portlet/src/main/webapp/vue-apps/notification-administration/js/NotificationAdministration.js
@@ -0,0 +1,86 @@
+/*
+ * This file is part of the Meeds project (https://meeds.io/).
+ *
+ * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+export function getSettings() {
+ return fetch(`${eXo.env.portal.context}/${eXo.env.portal.rest}/notifications/settings`, {
+ method: 'GET',
+ credentials: 'include',
+ }).then(resp => {
+ if (resp?.ok) {
+ return resp.json();
+ } else {
+ throw new Error('Error getting notification settings');
+ }
+ });
+}
+
+export function saveSenderEmail(name, email) {
+ const formData = new FormData();
+ formData.append('name', name);
+ formData.append('email', email);
+ const params = new URLSearchParams(formData).toString();
+ return fetch(`${eXo.env.portal.context}/${eXo.env.portal.rest}/notifications/settings`, {
+ method: 'PATCH',
+ credentials: 'include',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ body: params
+ }).then(resp => {
+ if (!resp?.ok) {
+ if (resp?.status === 400) {
+ return resp.text().then(e => {
+ throw new Error(e);
+ });
+ } else {
+ throw new Error('Error saving plugin settings');
+ }
+ }
+ });
+}
+
+export function savePluginSettings(pluginId, channels) {
+ return fetch(`${eXo.env.portal.context}/${eXo.env.portal.rest}/notifications/settings/plugin/${pluginId}`, {
+ method: 'PATCH',
+ credentials: 'include',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ body: `channels=${channels}`
+ }).then(resp => {
+ if (!resp?.ok) {
+ throw new Error('Error saving plugin settings');
+ }
+ });
+}
+
+export function saveChannelStatus(channelId, enable) {
+ return fetch(`${eXo.env.portal.context}/${eXo.env.portal.rest}/notifications/settings/channel/${channelId}`, {
+ method: 'PATCH',
+ credentials: 'include',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ body: `enable=${enable}`
+ }).then(resp => {
+ if (!resp?.ok) {
+ throw new Error('Error saving channel setting');
+ }
+ });
+}
diff --git a/webapp/portlet/src/main/webapp/vue-apps/notification-administration/main.js b/webapp/portlet/src/main/webapp/vue-apps/notification-administration/main.js
new file mode 100644
index 00000000000..0764e6908f2
--- /dev/null
+++ b/webapp/portlet/src/main/webapp/vue-apps/notification-administration/main.js
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the Meeds project (https://meeds.io/).
+ *
+ * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import './initComponents.js';
+import './services.js';
+
+// get overrided components if exists
+if (extensionRegistry) {
+ const components = extensionRegistry.loadComponents('NotificationAdministration');
+ if (components && components.length > 0) {
+ components.forEach(cmp => {
+ Vue.component(cmp.componentName, cmp.componentOptions);
+ });
+ }
+}
+
+document.dispatchEvent(new CustomEvent('displayTopBarLoading'));
+
+//getting language of user
+const lang = eXo && eXo.env.portal.language || 'en';
+
+//should expose the locale ressources as REST API
+const urls = [
+ `${eXo.env.portal.context}/${eXo.env.portal.rest}/i18n/bundle/locale.portlet.NotificationAdministration-${lang}.json`,
+ `${eXo.env.portal.context}/${eXo.env.portal.rest}/i18n/bundle/locale.portlet.UserNotificationPortlet-${lang}.json`,
+ `${eXo.env.portal.context}/${eXo.env.portal.rest}/i18n/bundle/locale.portlet.social.UserSettings-${lang}.json`,
+];
+
+const appId = 'NotificationAdministration';
+
+export function init(settings) {
+ exoi18n.loadLanguageAsync(lang, urls).then(i18n => {
+ Vue.createApp({
+ data: {
+ settings: settings,
+ },
+ mounted() {
+ document.dispatchEvent(new CustomEvent('hideTopBarLoading'));
+ },
+ template: ``,
+ i18n,
+ vuetify: Vue.prototype.vuetifyOptions,
+ }, `#${appId}`, 'Notification Administration');
+ });
+}
diff --git a/webapp/portlet/src/main/webapp/vue-apps/notification-administration/services.js b/webapp/portlet/src/main/webapp/vue-apps/notification-administration/services.js
new file mode 100644
index 00000000000..cd8b12e8fc9
--- /dev/null
+++ b/webapp/portlet/src/main/webapp/vue-apps/notification-administration/services.js
@@ -0,0 +1,24 @@
+/*
+ * This file is part of the Meeds project (https://meeds.io/).
+ *
+ * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import * as notificationAdministration from './js/NotificationAdministration.js';
+
+window.Object.defineProperty(Vue.prototype, '$notificationAdministration', {
+ value: notificationAdministration,
+});
diff --git a/webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/components/UserSettingNotificationChannel.vue b/webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/components/UserSettingNotificationChannel.vue
similarity index 100%
rename from webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/components/UserSettingNotificationChannel.vue
rename to webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/components/UserSettingNotificationChannel.vue
diff --git a/webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/components/UserSettingNotificationDrawer.vue b/webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/components/UserSettingNotificationDrawer.vue
similarity index 100%
rename from webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/components/UserSettingNotificationDrawer.vue
rename to webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/components/UserSettingNotificationDrawer.vue
diff --git a/webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/components/UserSettingNotificationGroup.vue b/webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/components/UserSettingNotificationGroup.vue
similarity index 100%
rename from webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/components/UserSettingNotificationGroup.vue
rename to webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/components/UserSettingNotificationGroup.vue
diff --git a/webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/components/UserSettingNotificationPlugin.vue b/webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/components/UserSettingNotificationPlugin.vue
similarity index 100%
rename from webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/components/UserSettingNotificationPlugin.vue
rename to webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/components/UserSettingNotificationPlugin.vue
diff --git a/webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/components/UserSettingNotifications.vue b/webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/components/UserSettingNotifications.vue
similarity index 96%
rename from webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/components/UserSettingNotifications.vue
rename to webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/components/UserSettingNotifications.vue
index fd3c4eedf42..2de58b96387 100644
--- a/webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/components/UserSettingNotifications.vue
+++ b/webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/components/UserSettingNotifications.vue
@@ -73,6 +73,9 @@ export default {
return fetch(`${eXo.env.portal.context}/${eXo.env.portal.rest}/notifications/settings/${eXo.env.portal.userName}`)
.then(resp => resp && resp.ok && resp.json())
.then(settings => {
+ if (this.displayed && !settings?.channels?.length) {
+ this.displayed = false;
+ }
this.notificationSettings = settings;
return this.$nextTick();
})
diff --git a/webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/components/UserSettingNotificationsWindow.vue b/webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/components/UserSettingNotificationsWindow.vue
similarity index 100%
rename from webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/components/UserSettingNotificationsWindow.vue
rename to webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/components/UserSettingNotificationsWindow.vue
diff --git a/webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/initComponents.js b/webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/initComponents.js
similarity index 100%
rename from webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/initComponents.js
rename to webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/initComponents.js
diff --git a/webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/main.js b/webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/main.js
similarity index 95%
rename from webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/main.js
rename to webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/main.js
index 79ae717566a..329b7087b25 100644
--- a/webapp/portlet/src/main/webapp/vue-apps/user-setting-notifications/main.js
+++ b/webapp/portlet/src/main/webapp/vue-apps/notification-user-settings/main.js
@@ -17,7 +17,7 @@ const lang = eXo && eXo.env.portal.language || 'en';
//should expose the locale ressources as REST API
const urls = [
- `${eXo.env.portal.context}/${eXo.env.portal.rest}/i18n/bundle/locale.portlet.notification.UserNotificationPortlet-${lang}.json`,
+ `${eXo.env.portal.context}/${eXo.env.portal.rest}/i18n/bundle/locale.portlet.UserNotificationPortlet-${lang}.json`,
`${eXo.env.portal.context}/${eXo.env.portal.rest}/i18n/bundle/locale.portlet.social.UserSettings-${lang}.json`
];
diff --git a/webapp/portlet/webpack.common.js b/webapp/portlet/webpack.common.js
index 58806cf22c9..c9c998dab68 100644
--- a/webapp/portlet/webpack.common.js
+++ b/webapp/portlet/webpack.common.js
@@ -45,7 +45,7 @@ let config = {
profileContactInformation: './src/main/webapp/vue-apps/profile-contact-information/main.js',
profileWorkExperience: './src/main/webapp/vue-apps/profile-work-experience/main.js',
userSettingLanguage: './src/main/webapp/vue-apps/user-setting-language/main.js',
- userSettingNotifications: './src/main/webapp/vue-apps/user-setting-notifications/main.js',
+ userSettingNotifications: './src/main/webapp/vue-apps/notification-user-settings/main.js',
userSettingSecurity: './src/main/webapp/vue-apps/user-setting-security/main.js',
userSettingTimezone: './src/main/webapp/vue-apps/user-setting-timezone/main.js',
spaceInfos: './src/main/webapp/vue-apps/space-infos-app/main.js',
@@ -73,6 +73,7 @@ let config = {
generalSettings: './src/main/webapp/vue-apps/general-settings/main.js',
imageCropper: './src/main/webapp/vue-apps/component-image-crop/main.js',
translationField: './src/main/webapp/vue-apps/component-translation-field/main.js',
+ notificationAdministration: './src/main/webapp/vue-apps/notification-administration/main.js',
},
module: {
rules: [