001/*
002 *  Copyright 2015 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.subscribe;
017
018import java.util.Collection;
019import java.util.Collections;
020import java.util.Date;
021import java.util.HashMap;
022import java.util.LinkedHashSet;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
026import java.util.UUID;
027import java.util.regex.Pattern;
028
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.commons.lang.StringUtils;
032
033import org.ametys.core.ui.Callable;
034import org.ametys.core.ui.StaticClientSideElement;
035import org.ametys.plugins.newsletter.category.Category;
036import org.ametys.plugins.newsletter.category.CategoryProviderExtensionPoint;
037import org.ametys.plugins.newsletter.daos.Subscriber;
038import org.ametys.plugins.newsletter.daos.SubscribersDAO;
039
040/**
041 * Client side element for newsletter subscribers
042 */
043public class SubscribersClientSideElement extends StaticClientSideElement
044{
045    private static final Pattern __EMAIL_VALIDATOR = Pattern.compile("^([a-z0-9._-]+@[a-z0-9.-]{2,}[.][a-zA-Z0-9]{2,})?$");
046    
047    /** The subscribers DAO. */
048    protected SubscribersDAO _subscribersDao;
049    
050    /** The category provider extension point. */
051    protected CategoryProviderExtensionPoint _categoryProviderEP;
052
053    @Override
054    public void service(ServiceManager smanager) throws ServiceException
055    {
056        super.service(smanager);
057        _subscribersDao = (SubscribersDAO) smanager.lookup(SubscribersDAO.ROLE);
058        _categoryProviderEP = (CategoryProviderExtensionPoint) smanager.lookup(CategoryProviderExtensionPoint.ROLE);
059    }
060    
061    /**
062     * Add a list of subscribers to the newsletter category of the specified site
063     * @param siteName The site name
064     * @param categoryId The category id
065     * @param emailsList The list of emails. Emails can be separeted by a new line or a semicolon.
066     * @return The result, with the count of subscribed or error emails.
067     */
068    @Callable
069    public Map<String, Object> addSubscribers (String siteName, String categoryId, String emailsList)
070    {
071        Map<String, Object> result = new HashMap<>();
072
073        // Test if the category exists.
074        Category category = _categoryProviderEP.getCategory(categoryId);
075        if (category == null)
076        {
077            return Collections.singletonMap("error", "unknown-category");
078        }
079        
080        // Extract the emails from the import file.
081        Collection<String> emails = getEmails(emailsList);
082        
083        if (!emails.isEmpty())
084        {
085            // Insert the emails.
086            insertSubscribers(emails, categoryId, siteName, result);
087        }
088        else
089        {
090            result.put("subscribedCount", "0");
091            result.put("existingCount", "0");
092            result.put("errorCount", "0");
093        }
094        
095        result.put("success", "true");
096        result.put("categoryId", categoryId);
097        
098        return result;
099    }
100    
101    /**
102     * Extract the emails from the field.
103     * @param emailsList the list of emails
104     * @return a collection of the emails.
105     */
106    protected Collection<String> getEmails(String emailsList)
107    {
108        Set<String> emails = new LinkedHashSet<>();
109        
110        String[] part = emailsList.split("[;\n]");
111        for (String line : part)
112        {
113            String email = StringUtils.trimToEmpty(line);
114            if (__EMAIL_VALIDATOR.matcher(email.toLowerCase()).matches() && StringUtils.isNotBlank(email))
115            {
116                emails.add(email);
117            }
118            else
119            {
120                getLogger().warn("Import subscribers email: '" + email + "' is not a valid email; it will be ignored");
121            }
122        }
123        
124        return emails;
125    }
126    
127
128    /**
129     * Insert subscribers
130     * @param emails the list of emails
131     * @param categoryId the id of the newsletter category
132     * @param siteName the name of the sites
133     * @param result the result map
134     */
135    protected void insertSubscribers(Collection<String> emails, String categoryId, String siteName, Map<String, Object> result)
136    {
137        int subscribedCount = 0;
138        int existingCount = 0;
139        int errorCount = 0;
140        
141        for (String email : emails)
142        {
143            if (_subscribersDao.getSubscriber(email, siteName, categoryId) == null)
144            {
145                Subscriber subscriber = new Subscriber();
146                subscriber.setEmail(email);
147                subscriber.setSiteName(siteName);
148                subscriber.setCategoryId(categoryId);
149                subscriber.setSubscribedAt(new Date());
150                
151                // Generate unique token.
152                String token = UUID.randomUUID().toString();
153                subscriber.setToken(token);
154                
155                _subscribersDao.subscribe(subscriber);
156                
157                if (getLogger().isInfoEnabled())
158                {
159                    getLogger().info("The user with email '" + email + "' subscribed to the newsletter with the token " + token);
160                }
161                
162                subscribedCount++;
163            }
164            else
165            {
166                existingCount++;
167            }
168        }
169        
170        result.put("subscribedCount", Integer.toString(subscribedCount));
171        result.put("existingCount", Integer.toString(existingCount));
172        result.put("errorCount", Integer.toString(errorCount));
173    }
174    
175    
176    /**
177     * Remove a list of subscribers from the newsletter category of the specified site
178     * @param siteName The site name
179     * @param categoryId The category id
180     * @param emails The list of emails.
181     * @return The error message, or an empty map if everything is ok.
182     */
183    @Callable
184    public Map<String, Object> removeSubscribers (String siteName, String categoryId, List<String> emails)
185    {
186        Map<String, Object> result = new HashMap<>();
187    
188        // Test if the category exists.
189        Category category = _categoryProviderEP.getCategory(categoryId);
190        if (category == null)
191        {
192            return Collections.singletonMap("message", "unknown-category");
193        }
194        
195        if (emails.size() > 0)
196        {
197            for (String email : emails)
198            {
199                Subscriber subscriber = _subscribersDao.getSubscriber(email, siteName, categoryId);
200                
201                if (subscriber != null)
202                {
203                    String token = subscriber.getToken();
204                    
205                    _subscribersDao.unsubscribe(token);
206                    
207                    if (getLogger().isInfoEnabled())
208                    {
209                        getLogger().info("The user with email '" + email + "' unsubscribed from the newsletter in category " + categoryId + " of site " + siteName);
210                    }
211                }
212                else
213                {
214                    getLogger().error("Impossible to find and remove from the newsletter the subscriber with email '" + email + "' in category " + categoryId + " of site " + siteName);
215                }
216            }
217        }
218        
219        return result;
220    }
221}