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}