001/*
002 *  Copyright 2010 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.ArrayList;
019import java.util.Date;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024import java.util.UUID;
025import java.util.regex.Matcher;
026import java.util.regex.Pattern;
027
028import org.apache.avalon.framework.parameters.Parameters;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.cocoon.acting.ServiceableAction;
032import org.apache.cocoon.environment.ObjectModelHelper;
033import org.apache.cocoon.environment.Redirector;
034import org.apache.cocoon.environment.Request;
035import org.apache.cocoon.environment.SourceResolver;
036import org.apache.commons.lang.StringUtils;
037
038import org.ametys.plugins.newsletter.category.Category;
039import org.ametys.plugins.newsletter.category.CategoryProvider;
040import org.ametys.plugins.newsletter.category.CategoryProviderExtensionPoint;
041import org.ametys.plugins.newsletter.daos.Subscriber;
042import org.ametys.plugins.newsletter.daos.SubscribersDAO;
043import org.ametys.plugins.repository.AmetysObjectResolver;
044import org.ametys.core.captcha.CaptchaHelper;
045import org.ametys.web.WebConstants;
046import org.ametys.web.cache.PageHelper;
047import org.ametys.web.repository.page.Page;
048import org.ametys.web.repository.page.ZoneItem;
049import org.ametys.web.site.SiteConfigurationExtensionPoint;
050
051/**
052 * This action subscribes an email address to a newsletter
053 *
054 */
055public class SubscribeAction extends ServiceableAction
056{
057    /** The email pattern */
058    public static final Pattern EMAIL_PATTERN = Pattern.compile("^([a-zA-Z0-9]+(([\\.\\-\\_]?[a-zA-Z0-9]+)+)?)\\@(([a-zA-Z0-9]+[\\.\\-\\_])+[a-zA-Z0-9]{2,})$");
059    /** The subscribers DAO */
060    protected SubscribersDAO _subscribersDao;
061    /** The category providers manager */
062    protected CategoryProviderExtensionPoint _categoryProviderEP;
063    /** The site configuration */
064    protected SiteConfigurationExtensionPoint _siteConfiguration;
065    /** The Ametys object resolver */
066    protected AmetysObjectResolver _resolver;
067    /** Page helper */
068    protected PageHelper _pageHelper;
069    
070    @Override
071    public void service(ServiceManager smanager) throws ServiceException
072    {
073        super.service(smanager);
074        _subscribersDao = (SubscribersDAO) smanager.lookup(SubscribersDAO.ROLE);
075        _categoryProviderEP = (CategoryProviderExtensionPoint) smanager.lookup(CategoryProviderExtensionPoint.ROLE);
076        _siteConfiguration = (SiteConfigurationExtensionPoint) smanager.lookup(SiteConfigurationExtensionPoint.ROLE);
077        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
078        _pageHelper = (PageHelper) smanager.lookup(PageHelper.ROLE);
079    }
080    
081    @Override
082    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
083    {
084        Map<String, String> result = new HashMap<>();
085        
086        Request request = ObjectModelHelper.getRequest(objectModel);
087        boolean subscribe = request.getParameter("subscribe") != null;
088        
089        String email = request.getParameter("email");
090        result.put("email", email);
091        
092        ZoneItem currentZoneItem = (ZoneItem) request.getAttribute(WebConstants.REQUEST_ATTR_ZONEITEM);
093        String zoneItemId = request.getParameter("zoneitem-id");
094
095        if (subscribe && (zoneItemId == null || currentZoneItem.getId().equals(zoneItemId))) // Handle zoneitem id null for legacy purpose
096        {
097            try
098            {
099                String siteName = request.getParameter("siteName");
100                String[] categories = request.getParameterValues("category");
101                
102                // Validate email
103                if (!_validEmail(email))
104                {
105                    result.put("msg", "invalid-email");
106                    return result;
107                }
108                
109                Page page = _resolver.resolveById(request.getParameter("page-id"));
110                if (_pageHelper.isCaptchaRequired(page))
111                {
112                    String captchaKey = request.getParameter("captcha-key");
113                    String answer = request.getParameter("captcha");
114        
115                    // Validate captcha 
116                    if (!CaptchaHelper.checkAndInvalidate(captchaKey, answer))
117                    {
118                        result.put("msg", "invalid-captcha");
119                        return result;
120                    }
121                }
122                
123                // Validate categories
124                if (!_validCategory(categories))
125                {
126                    result.put("msg", "invalid-category");
127                    return result;
128                }
129                
130                
131                List<String> subscribeTo = new ArrayList<>();
132                List<String> alreadySubscribeTo = new ArrayList<>();
133                for (String categoryID : categories)
134                {
135                    if (_subscribersDao.getSubscriber(email, siteName, categoryID) == null)
136                    {
137                        Category category = _getCategory(categoryID);
138                        if (category != null)
139                        {
140                            Subscriber subscriber = new Subscriber();
141                            subscriber.setEmail(email);
142                            subscriber.setSiteName(siteName);
143                            subscriber.setCategoryId(categoryID);
144                            subscriber.setSubscribedAt(new Date());
145                            
146                            // Generate unique token
147                            String token = UUID.randomUUID().toString();
148                            subscriber.setToken(token);
149                            
150                            _subscribersDao.subscribe(subscriber);
151                            
152                            getLogger().info("The user with email '" + email + "' subscribed to the newsletter with the token " + token);
153                            
154                            subscribeTo.add(categoryID);
155                        }
156                    }
157                    else
158                    {
159                        alreadySubscribeTo.add(categoryID);
160                    }
161                }
162                
163                result.put("msg", "success");
164                String ids = "";
165                int i = 0;
166                for (String id : subscribeTo)
167                {
168                    if (i != 0)
169                    {
170                        ids += ",";
171                    }
172                    ids += id;
173                    i++;
174                }
175                result.put("subscribeTo", ids);
176                
177                ids = "";
178                i = 0;
179                for (String id : alreadySubscribeTo)
180                {
181                    if (i != 0)
182                    {
183                        ids += ",";
184                    }
185                    ids += id;
186                    i++;
187                }
188                result.put("alreadySubscribeTo", ids);
189            }
190            catch (Exception e)
191            {
192                result.put("msg", "failure");
193                getLogger().error("An error occurred during the subscription for the email '" + email + "'", e);
194            }
195        }
196        return result;
197    }
198    
199    /**
200     * Determines if the email address is valid
201     * @param email The email address
202     * @return true if the email is valid
203     */
204    protected boolean _validEmail (String email)
205    {
206        if (StringUtils.isEmpty(email))
207        {
208            return false;
209        }
210        
211        Matcher matcher = EMAIL_PATTERN.matcher(email);
212        return matcher.matches();
213    }
214    
215    /**
216     * Determines if the categories are valid
217     * @param categories The categories id
218     * @return true if the categories are valid
219     */
220    protected boolean _validCategory (String[] categories)
221    {
222        return categories != null && categories.length > 0;
223    }
224    
225
226    /**
227     * Get the category
228     * @param categoryID The category id
229     * @return the category
230     */
231    protected Category _getCategory (String categoryID)
232    {
233        Set<String> ids = _categoryProviderEP.getExtensionsIds();
234        for (String id : ids)
235        {
236            CategoryProvider provider = _categoryProviderEP.getExtension(id);
237            if (provider.hasCategory(categoryID))
238            {
239                return provider.getCategory(categoryID);
240            }
241        }
242        
243        return null;
244    }
245}