001/* 002 * Copyright 2021 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.workspaces.project.notification.preferences; 017 018import java.util.Collections; 019import java.util.Map; 020import java.util.Set; 021import java.util.stream.Collectors; 022 023import org.apache.avalon.framework.component.Component; 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.avalon.framework.service.Serviceable; 027import org.apache.commons.lang3.StringUtils; 028 029import org.ametys.core.user.User; 030import org.ametys.core.user.UserIdentity; 031import org.ametys.core.userpref.UserPreferencesException; 032import org.ametys.core.userpref.UserPreferencesExtensionPoint; 033import org.ametys.core.userpref.UserPreferencesManager; 034import org.ametys.core.util.JSONUtils; 035import org.ametys.plugins.workspaces.project.ProjectManager; 036import org.ametys.plugins.workspaces.project.objects.Project; 037import org.ametys.runtime.plugin.component.AbstractLogEnabled; 038 039/** 040 * Helper for notifications preferences 041 * 042 */ 043public class NotificationPreferencesHelper extends AbstractLogEnabled implements Component, Serviceable 044{ 045 /** the id of the user prefs for notification preferences */ 046 public static final String USERPREFS_ID = "workspaces.notifications"; 047 048 /** The avalon role */ 049 public static final String ROLE = NotificationPreferencesHelper.class.getName(); 050 051 /** The JSON utils */ 052 protected JSONUtils _jsonUtils; 053 /** The user preferences manager */ 054 protected UserPreferencesManager _userPrefManager; 055 /** The user preferences extension point */ 056 protected UserPreferencesExtensionPoint _userPrefEP; 057 /** The project manager */ 058 protected ProjectManager _projectManager; 059 060 /** 061 * The frequency of notifications 062 * 063 */ 064 public static enum Frequency 065 { 066 /** Each frequency */ 067 EACH, 068 /** Daily frequency */ 069 DAILY, 070 /** Weekly frequency */ 071 WEEKLY, 072 /** Monthly frequency */ 073 MONTHLY 074 } 075 076 public void service(ServiceManager manager) throws ServiceException 077 { 078 _userPrefManager = (UserPreferencesManager) manager.lookup(UserPreferencesManager.ROLE + ".FO"); 079 _userPrefEP = (UserPreferencesExtensionPoint) manager.lookup(UserPreferencesExtensionPoint.ROLE + ".FO"); 080 _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE); 081 _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE); 082 } 083 084 /** 085 * Determines if a user asked to be notify for a given project and frequency 086 * @param user the user 087 * @param projectName the project name 088 * @param frequency the required frequency (each, daily, weekly or monthly) 089 * @return true if the user need to be notified 090 */ 091 public boolean askedToBeNotified(User user, String projectName, Frequency frequency) 092 { 093 return askedToBeNotified(user.getIdentity(), projectName, frequency); 094 } 095 096 /** 097 * Determines if a user asked to be notify for a given project and frequency 098 * @param userIdentity the user identity 099 * @param projectName the project name 100 * @param frequency the required frequency (each, daily, weekly or monthly). Cannot be null. 101 * @return true if the user need to be notified 102 */ 103 public boolean askedToBeNotified(UserIdentity userIdentity, String projectName, Frequency frequency) 104 { 105 try 106 { 107 Map<String, Object> notificationPreferences = getNotificationPreferences(userIdentity); 108 109 if ((Boolean) notificationPreferences.get("disable")) 110 { 111 return false; 112 } 113 114 @SuppressWarnings("unchecked") 115 Map<String, Object> projectsPreferences = (Map<String, Object>) notificationPreferences.get("projects"); 116 if (!projectsPreferences.isEmpty() && projectsPreferences.get(projectName) != null) 117 { 118 @SuppressWarnings("unchecked") 119 Map<String, Object> projectPreference = (Map<String, Object>) projectsPreferences.get(projectName); 120 return !(Boolean) projectPreference.get("disable") && StringUtils.equalsIgnoreCase(frequency.name(), (String) (projectPreference.get("frequency"))); 121 } 122 else 123 { 124 return StringUtils.equalsIgnoreCase(frequency.name(), (String) notificationPreferences.get("frequency")); 125 } 126 } 127 catch (UserPreferencesException e) 128 { 129 getLogger().warn("Failed to retrieve the user preferences for user {}", UserIdentity.userIdentityToString(userIdentity), e); 130 return false; 131 } 132 } 133 134 /** 135 * Determines if a user has at least one notification preference with the given frequency 136 * @param user the user 137 * @param frequency the frequency 138 * @return true if the user has at least one notification preference with the given frequency 139 */ 140 public boolean hasFrequencyInPreferences(User user, Frequency frequency) 141 { 142 // TODO We should use a cache here to avoid computing the userPref multiple time for the same user 143 try 144 { 145 String notificationPreferencesJSON = getNotificationPreferencesAsString(user); 146 return notificationPreferencesJSON.contains(frequency.name().toLowerCase()); 147 } 148 catch (UserPreferencesException e) 149 { 150 getLogger().warn("Failed to retrieve the user preferences for user {}", user.getFullName(), e); 151 } 152 return false; 153 } 154 155 private boolean _isDisabled(Map<String, Map<String, Object>> projectPrefs, String projectName) 156 { 157 return projectPrefs.containsKey(projectName) && (Boolean) projectPrefs.get(projectName).get("disable"); 158 } 159 160 private boolean _matchFrequency(Map<String, Map<String, Object>> projectPrefs, String projectName, Frequency frequency) 161 { 162 return projectPrefs.containsKey(projectName) && StringUtils.equalsIgnoreCase(frequency.name(), (String) projectPrefs.get(projectName).get("frequency")); 163 } 164 165 /** 166 * Get the user's project set to receive notification for given frequency 167 * @param user the user 168 * @param frequency the matching frequency 169 * @return The names of user's project's with same frequency 170 */ 171 public Set<String> getUserProjectsWithFrequency(User user, Frequency frequency) 172 { 173 try 174 { 175 Map<String, Object> notificationPreferences = getNotificationPreferences(user); 176 if ((Boolean) notificationPreferences.get("disable")) 177 { 178 // Notifications are disabled for user 179 return Collections.EMPTY_SET; 180 } 181 182 String globalFrequency = (String) notificationPreferences.get("frequency"); 183 @SuppressWarnings("unchecked") 184 Map<String, Map<String, Object>> customProjectsPrefs = (Map<String, Map<String, Object>>) notificationPreferences.get("projects"); 185 186 if (globalFrequency.equals(frequency.name().toLowerCase())) 187 { 188 // Get all user projects and filter custom projects 189 Set<Project> userProjects = _projectManager.getUserProjects(user.getIdentity()).keySet(); 190 191 return userProjects.stream() 192 .map(Project::getName) 193 .filter(p -> !_isDisabled(customProjectsPrefs, p)) 194 .filter(p -> !customProjectsPrefs.containsKey(p) || _matchFrequency(customProjectsPrefs, p, frequency)) 195 .collect(Collectors.toSet()); 196 } 197 else 198 { 199 // Get custom projects with same frequency 200 return customProjectsPrefs.entrySet() 201 .stream() 202 .filter(e -> !(Boolean) e.getValue().get("disable")) 203 .filter(e -> frequency.name().toLowerCase().equals(e.getValue().get("frequency"))) 204 .map(e -> e.getKey()) 205 .collect(Collectors.toSet()); 206 207 } 208 } 209 catch (UserPreferencesException e) 210 { 211 getLogger().warn("Failed to retrieve the user preferences for user {}", user.getFullName(), e); 212 } 213 214 return Collections.EMPTY_SET; 215 } 216 217 /** 218 * Get the user's notification preferences 219 * @param user the user 220 * @return the user's notification preferences 221 * @throws UserPreferencesException if failed to retrieve user's preferences 222 */ 223 public Map<String, Object> getNotificationPreferences(User user) throws UserPreferencesException 224 { 225 return getNotificationPreferences(user.getIdentity()); 226 } 227 228 /** 229 * Get the user's notification preferences 230 * @param userIdentity the user identity 231 * @return the user's notification preferences 232 * @throws UserPreferencesException if failed to retrieve user's preference 233 */ 234 public Map<String, Object> getNotificationPreferences(UserIdentity userIdentity) throws UserPreferencesException 235 { 236 String prefAsString = getNotificationPreferencesAsString(userIdentity); 237 return _jsonUtils.convertJsonToMap(prefAsString); 238 } 239 240 /** 241 * Get the user's notification preferences as json String 242 * @param user the user 243 * @return the user's notification preferences as json String 244 * @throws UserPreferencesException if failed to retrieve user's preference 245 */ 246 public String getNotificationPreferencesAsString(User user) throws UserPreferencesException 247 { 248 return getNotificationPreferencesAsString(user.getIdentity()); 249 } 250 251 252 /** 253 * Get the user's notification preferences as json String 254 * @param userIdentity the user identity 255 * @return the user's notification preferences as json String 256 * @throws UserPreferencesException if failed to retrieve user's preference 257 */ 258 public String getNotificationPreferencesAsString(UserIdentity userIdentity) throws UserPreferencesException 259 { 260 String notificationPreferencesJSON = _userPrefManager.getUserPreferenceAsString(userIdentity, _getStorageContext(), Map.of(), USERPREFS_ID); 261 if (notificationPreferencesJSON == null) 262 { 263 // get default value 264 notificationPreferencesJSON = (String) _userPrefEP.getUserPreference(Map.of(), USERPREFS_ID).getDefaultValue(); 265 } 266 267 return notificationPreferencesJSON; 268 } 269 270 /** 271 * Delete the user preferences for notification on the given project 272 * @param userIdentity the user identity 273 * @param projectName the name of the project to delete 274 */ 275 public void deleteProjectNotificationPreferences(UserIdentity userIdentity, String projectName) 276 { 277 try 278 { 279 Map<String, String> values = _userPrefManager.getUnTypedUserPrefs(userIdentity, _getStorageContext(), Map.of()); 280 281 String notificationPreferencesJSON = values.get(USERPREFS_ID); 282 Map<String, Object> notificationPreferences = _jsonUtils.convertJsonToMap(notificationPreferencesJSON); 283 if (notificationPreferences.containsKey("projects")) 284 { 285 @SuppressWarnings("unchecked") 286 Map<String, Object> projectsPreferences = (Map<String, Object>) notificationPreferences.get("projects"); 287 if (projectsPreferences.containsKey(projectName)) 288 { 289 projectsPreferences.remove(projectName); 290 notificationPreferencesJSON = _jsonUtils.convertObjectToJson(notificationPreferences); 291 292 values.put(USERPREFS_ID, notificationPreferencesJSON); 293 294 _userPrefManager.setUserPreferences(userIdentity, _getStorageContext(), Map.of(), values); 295 } 296 } 297 } 298 catch (UserPreferencesException e) 299 { 300 getLogger().warn("Failed to delete the notification user preference for project {} of user {}", projectName, UserIdentity.userIdentityToString(userIdentity), e); 301 } 302 } 303 304 private String _getStorageContext() 305 { 306 return "/sites/" + _projectManager.getCatalogSiteName(); 307 } 308 309 310}