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