001/*
002 *  Copyright 2012 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.newsletter.userpref;
017
018import java.time.ZonedDateTime;
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.Date;
022import java.util.HashMap;
023import java.util.HashSet;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027import java.util.UUID;
028
029import org.apache.avalon.framework.component.Component;
030import org.apache.avalon.framework.logger.AbstractLogEnabled;
031import org.apache.avalon.framework.service.ServiceException;
032import org.apache.avalon.framework.service.ServiceManager;
033import org.apache.avalon.framework.service.Serviceable;
034
035import org.ametys.core.user.User;
036import org.ametys.core.user.UserIdentity;
037import org.ametys.core.user.UserManager;
038import org.ametys.core.user.directory.NotUniqueUserException;
039import org.ametys.core.user.population.PopulationContextHelper;
040import org.ametys.core.userpref.UserPreferencesException;
041import org.ametys.core.userpref.UserPreferencesStorage;
042import org.ametys.plugins.newsletter.category.CategoryProviderExtensionPoint;
043import org.ametys.plugins.newsletter.daos.Subscriber;
044import org.ametys.plugins.newsletter.daos.SubscribersDAO;
045import org.ametys.plugins.newsletter.daos.SubscribersDAO.UnsubscribeOrigin;
046import org.ametys.plugins.repository.AmetysObjectIterable;
047import org.ametys.web.repository.site.Site;
048import org.ametys.web.repository.site.SiteManager;
049import org.ametys.web.userpref.FOUserPreferencesConstants;
050
051/**
052 * Retrieves and stores newsletter user preferences values as subscriptions.
053 */
054public class NewsletterUserPreferencesStorage extends AbstractLogEnabled implements UserPreferencesStorage, Component, Serviceable
055{
056    
057    /** The front-office users manager. */
058    protected UserManager _foUserManager;
059    
060    /** The category provider extension point. */
061    protected CategoryProviderExtensionPoint _categoryEP;
062    
063    /** The site manager */
064    protected SiteManager _siteManager;
065    
066    /** The subscribers DAO. */
067    protected SubscribersDAO _subscribersDao;
068
069    /** The population context helper */
070    protected PopulationContextHelper _populationContextHelper;
071    
072    @Override
073    public void service(ServiceManager serviceManager) throws ServiceException
074    {
075        _foUserManager = (UserManager) serviceManager.lookup(UserManager.ROLE);
076        _categoryEP = (CategoryProviderExtensionPoint) serviceManager.lookup(CategoryProviderExtensionPoint.ROLE);
077        _populationContextHelper = (PopulationContextHelper) serviceManager.lookup(PopulationContextHelper.ROLE);
078        _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE);
079        _subscribersDao = (SubscribersDAO) serviceManager.lookup(SubscribersDAO.ROLE);
080    }
081    
082    @Override
083    public Map<String, String> getUnTypedUserPrefs(UserIdentity userIdentity, String storageContext, Map<String, String> contextVars) throws UserPreferencesException
084    {
085        try
086        {
087            Map<String, String> preferenceValues = new HashMap<>();
088            
089            User user = _foUserManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin());
090            String siteName = contextVars.get(FOUserPreferencesConstants.CONTEXT_VAR_SITENAME);
091            
092            if (user != null && siteName != null)
093            {
094                List<Subscriber> subscriptions = _subscribersDao.getSubscriptions(user.getEmail(), siteName);
095                for (Subscriber subscription : subscriptions)
096                {
097                    preferenceValues.put(subscription.getCategoryId(), "true");
098                }
099            }
100            
101            return preferenceValues;
102        }
103        catch (Exception e)
104        {
105            String message = "Error getting newsletter user preferences for login " + userIdentity + " and context " + storageContext;
106            getLogger().error(message, e);
107            throw new UserPreferencesException(message, e);
108        }
109    }
110    
111    @Override
112    public void setUserPreferences(UserIdentity userIdentity, String storageContext, Map<String, String> contextVars, Map<String, String> preferences) throws UserPreferencesException
113    {
114        try
115        {
116            User user = _foUserManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin());
117            String siteName = contextVars.get(FOUserPreferencesConstants.CONTEXT_VAR_SITENAME);
118            
119            if (user != null && siteName != null)
120            {
121                List<Subscriber> newSubscribers = new ArrayList<>();
122                Set<String> removeTokens = new HashSet<>();
123                
124                Map<String, String> existingCategoryIds = getExistingCategoryIds(user.getEmail(), siteName);
125                
126                for (String categoryId : preferences.keySet())
127                {
128                    String value = preferences.get(categoryId);
129                    
130                    if (value.equals("true") && _categoryEP.hasCategory(categoryId) && !existingCategoryIds.containsKey(categoryId))
131                    {
132                        Subscriber subscriber = getSubscription(siteName, user, categoryId);
133                        newSubscribers.add(subscriber);
134                    }
135                    else if (!value.equals("true") && _categoryEP.hasCategory(categoryId) && existingCategoryIds.containsKey(categoryId))
136                    {
137                        String token = existingCategoryIds.get(categoryId);
138                        removeTokens.add(token);
139                    }
140                }
141                
142                // Modify the subscriptions.
143                _subscribersDao.modifySubscriptions(newSubscribers, removeTokens, UnsubscribeOrigin.SUBSCRIBER);
144            }
145        }
146        catch (Exception e)
147        {
148            String message = "Error setting newsletter user preferences for login " + userIdentity + " and context " + storageContext;
149            getLogger().error(message, e);
150            throw new UserPreferencesException(message, e);
151        }
152    }
153    
154    @Override
155    public void removeUserPreferences(UserIdentity userIdentity, String storageContext, Map<String, String> contextVars) throws UserPreferencesException
156    {
157        try
158        {
159            User user = _foUserManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin());
160            
161            if (user != null)
162            {
163                String email = user.getEmail();
164                if (email != null)
165                {
166                    String siteName = contextVars.get(FOUserPreferencesConstants.CONTEXT_VAR_SITENAME);
167                    if (siteName != null)
168                    {
169                        // Remove the subscriptions.
170                        _subscribersDao.unsubscribe(email, storageContext, UnsubscribeOrigin.DATAPOLICY);
171                    }
172                    else
173                    {
174                        try (AmetysObjectIterable<Site> sites = _siteManager.getSites())
175                        {
176                            for (Site site : sites)
177                            {
178                                // Before unsubscribing, check that no other user use the same email on the site
179                                siteName = site.getName();
180                                Set<String> userPopulationsOnSite = _populationContextHelper.getUserPopulationsOnContexts(Arrays.asList("/sites/" + siteName, "/sites-fo/" + siteName), false, false);
181                                try
182                                {
183                                    if (_foUserManager.getUserByEmail(userPopulationsOnSite, email) == null)
184                                    {
185                                        _subscribersDao.unsubscribe(email, siteName, UnsubscribeOrigin.DATAPOLICY);
186                                    }
187                                }
188                                catch (NotUniqueUserException e)
189                                {
190                                    // Do nothing, this email is still matching existing users
191                                }
192                            }
193                        }
194                    }
195                }
196            }
197        }
198        catch (Exception e)
199        {
200            String message = "Error removing newsletter subscriptions for login " + userIdentity + " and context " + storageContext;
201            getLogger().error(message, e);
202            throw new UserPreferencesException(message, e);
203        }
204    }
205    
206    @Override
207    public String getUserPreferenceAsString(UserIdentity userIdentity, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException
208    {
209        String value = null;
210        
211        Boolean booleanValue = getUserPreferenceAsBoolean(userIdentity, storageContext, contextVars, id);
212        
213        if (booleanValue != null)
214        {
215            value = booleanValue.toString();
216        }
217        
218        return value;
219    }
220    
221    @Override
222    public Long getUserPreferenceAsLong(UserIdentity userIdentity, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException
223    {
224        return null;
225    }
226    
227    @Override
228    public ZonedDateTime getUserPreferenceAsDate(UserIdentity userIdentity, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException
229    {
230        return null;
231    }
232    
233    @Override
234    public Boolean getUserPreferenceAsBoolean(UserIdentity userIdentity, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException
235    {
236        try
237        {
238            Boolean value = null;
239            
240            User user = _foUserManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin());
241            String siteName = contextVars.get(FOUserPreferencesConstants.CONTEXT_VAR_SITENAME);
242            
243            if (user != null && siteName != null)
244            {
245                Subscriber subscriber = _subscribersDao.getSubscriber(user.getEmail(), siteName, id);
246                
247                value = Boolean.valueOf(subscriber != null);
248            }
249            
250            return value;
251        }
252        catch (Exception e)
253        {
254            throw new UserPreferencesException("Error getting newsletter user preferences for login " + userIdentity + " and context " + storageContext, e);
255        }
256    }
257    
258    @Override
259    public Double getUserPreferenceAsDouble(UserIdentity userIdentity, String storageContext, Map<String, String> contextVars, String id) throws UserPreferencesException
260    {
261        return null;
262    }
263    
264    /**
265     * Create a subscriber object from the given input.
266     * @param siteName the site name.
267     * @param user the user.
268     * @param categoryId the category ID.
269     * @return the Subscriber object.
270     */
271    protected Subscriber getSubscription(String siteName, User user, String categoryId)
272    {
273        return getSubscription(siteName, user, categoryId, true);
274    }
275    
276    /**
277     * Create a subscriber object from the given input.
278     * @param siteName the site name.
279     * @param user the user.
280     * @param categoryId the category ID.
281     * @param generateDateAndToken true to generate a token and set the subscription date, false otherwise.
282     * @return the Subscriber object.
283     */
284    protected Subscriber getSubscription(String siteName, User user, String categoryId, boolean generateDateAndToken)
285    {
286        Subscriber subscriber = new Subscriber();
287        subscriber.setEmail(user.getEmail());
288        subscriber.setSiteName(siteName);
289        subscriber.setCategoryId(categoryId);
290        
291        if (generateDateAndToken)
292        {
293            subscriber.setSubscribedAt(new Date());
294            
295            // Generate unique token.
296            String token = UUID.randomUUID().toString();
297            subscriber.setToken(token);
298        }
299        
300        return subscriber;
301    }
302    
303    /**
304     * Get the existing subscriptions for a user in a given site.
305     * @param email the user e-mail address.
306     * @param siteName the site name.
307     * @return a Set of category IDs, to which user has subscribed.
308     */
309    protected Map<String, String> getExistingCategoryIds(String email, String siteName)
310    {
311        Map<String, String> existingCategoryIds = new HashMap<>();
312        
313        List<Subscriber> existingSubscriptions = _subscribersDao.getSubscriptions(email, siteName);
314        for (Subscriber subscription : existingSubscriptions)
315        {
316            existingCategoryIds.put(subscription.getCategoryId(), subscription.getToken());
317        }
318        
319        return existingCategoryIds;
320    }
321}