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.workflow;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024
025import javax.jcr.RepositoryException;
026
027import org.apache.avalon.framework.context.Context;
028import org.apache.avalon.framework.context.ContextException;
029import org.apache.avalon.framework.context.Contextualizable;
030import org.apache.avalon.framework.service.ServiceException;
031import org.apache.avalon.framework.service.ServiceManager;
032import org.apache.cocoon.components.ContextHelper;
033import org.apache.cocoon.environment.Request;
034import org.apache.commons.lang3.Strings;
035
036import org.ametys.cms.workflow.AbstractContentWorkflowComponent;
037import org.ametys.plugins.newsletter.NewsletterDAO;
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.data.holder.ModifiableDataHolder;
044import org.ametys.plugins.workflow.EnhancedFunction;
045import org.ametys.runtime.i18n.I18nizableText;
046import org.ametys.web.repository.content.jcr.DefaultWebContent;
047import org.ametys.web.repository.site.Site;
048
049import com.opensymphony.module.propertyset.PropertySet;
050import com.opensymphony.workflow.WorkflowException;
051
052/**
053 * OSWorkflow function for creating a content.
054 */
055public class SendNewsletterFunction extends AbstractContentWorkflowComponent implements EnhancedFunction, Contextualizable
056{
057    private SubscribersDAO _subscribersDAO;
058    private CategoryProviderExtensionPoint _categoryProviderEP;
059    
060    private Context _context;
061    private NewsletterDAO _newsletterDAO;
062    
063    @Override
064    public void service(ServiceManager manager) throws ServiceException
065    {
066        super.service(manager);
067        _subscribersDAO = (SubscribersDAO) manager.lookup(SubscribersDAO.ROLE);
068        _categoryProviderEP = (CategoryProviderExtensionPoint) manager.lookup(CategoryProviderExtensionPoint.ROLE);
069        _newsletterDAO = (NewsletterDAO) manager.lookup(NewsletterDAO.ROLE);
070    }
071
072    public void contextualize(Context context) throws ContextException
073    {
074        _context = context;
075    }
076    
077    @Override
078    public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException
079    {
080        Request request = _getRequest();
081        DefaultWebContent content = (DefaultWebContent) getContent(transientVars);
082        ModifiableDataHolder internalDataHolder = content.getInternalDataHolder();
083        Boolean sent = internalDataHolder.getValue("sent");
084        
085        String sendRequestParam = request.getParameter("send");
086        if (sendRequestParam == null)
087        {
088            sendRequestParam = (String) request.getAttribute("send");
089        }
090        
091        // Newsletter has been sent once. resend only if explicitly requested
092        if (Boolean.TRUE.equals(sent))
093        {
094            if (!Strings.CS.equals(sendRequestParam, "true"))
095            {
096                return;
097            }
098        }
099        // Newsletter was never sent. do not send only if explicitly requested
100        else if (Strings.CS.equals(sendRequestParam, "false"))
101        {
102            return;
103        }
104        
105        try
106        {
107            // Subscribers
108            String categoryID = internalDataHolder.getValue("category");
109            String siteName = content.getSiteName();
110            Site site = content.getSite();
111
112            boolean descending = site.getValueOrDefault("newsletter-subscription-descending", false);
113            boolean ascending = site.getValueOrDefault("newsletter-subscription-ascending", false);
114            
115            List<Subscriber> subscribers = _subscribersDAO.getSubscribers(content.getSiteName(), internalDataHolder.getValue("category"));
116            if (descending)
117            {
118                subscribers.addAll(_getSubscribersOfParentCategories(categoryID, siteName));
119            }
120            if (ascending)
121            {
122                subscribers.addAll(_getSubscribersOfChildCategories(categoryID, siteName));
123            }
124
125            Map<String, String> recipients = new HashMap<>();
126            for (Subscriber subcriber : subscribers)
127            {
128                recipients.put(subcriber.getEmail(), subcriber.getToken());
129            }
130
131            _newsletterDAO.sendNewsletter(content, recipients);
132            
133            internalDataHolder.setValue("sent", true);
134            try
135            {
136                _newsletterDAO.removeTestNewsletter(content, site);
137            }
138            catch (RepositoryException e)
139            {
140                _logger.warn("Unable to remove the temporary test newsletter.", e);
141            }
142            content.saveChanges();
143            
144            // Send a google analytics event for every newsletter e-mail sent.
145            SendWebAnalyticsEventsEngine sendGaEngine = new SendWebAnalyticsEventsEngine();
146            sendGaEngine.initialize(_manager, _context);
147            sendGaEngine.parametrize(siteName, content, _newsletterDAO.getCategory(categoryID), recipients.size());
148            
149            new Thread(sendGaEngine).start();
150        }
151        catch (IOException e)
152        {
153            throw new WorkflowException("Unable to send mails !", e);
154        }
155        catch (ContextException e)
156        {
157            _logger.warn("Context exception when initializing an engine.", e);
158        }
159        catch (ServiceException e)
160        {
161            _logger.warn("Service exception when initializing an engine.", e);
162        }
163        
164    }
165    
166    /**
167     * Get the subscribers of parent categories
168     * @param categoryID The category id
169     * @param siteName The site name
170     * @return the subscribers of parent categories
171     */
172    protected List<Subscriber> _getSubscribersOfParentCategories (String categoryID, String siteName)
173    {
174        List<Subscriber> subscribers = new ArrayList<>();
175        
176        Category category = _newsletterDAO.getCategory(categoryID);
177        Category parentCategory = _newsletterDAO.getCategory(category.getParentId());
178        while (parentCategory != null)
179        {
180            subscribers.addAll(_subscribersDAO.getSubscribers(siteName, parentCategory.getId()));
181            parentCategory = _newsletterDAO.getCategory(parentCategory.getParentId());
182        }
183        
184        return subscribers;
185    }
186    
187    /**
188     * Get the subscribers of child categories
189     * @param categoryID The category id
190     * @param siteName The site name
191     * @return The subscribers of child categories
192     */
193    protected List<Subscriber> _getSubscribersOfChildCategories (String categoryID, String siteName)
194    {
195        List<Subscriber> subscribers = new ArrayList<>();
196        
197        CategoryProvider provider = _getProvider(categoryID);
198        
199        List<Category> children = provider.getCategories(categoryID);
200        for (Category child : children)
201        {
202            subscribers.addAll(_subscribersDAO.getSubscribers(siteName, child.getId()));
203            subscribers.addAll(_getSubscribersOfChildCategories (child.getId(), siteName));
204        }
205        
206        return subscribers;
207    }
208
209    /**
210     * Retrieve the request from which this component is called.
211     * @return the request
212     */
213    protected Request _getRequest()
214    {
215        return ContextHelper.getRequest(_context);
216    }
217    
218    /**
219     * Get the category provider
220     * @param categoryID The category id
221     * @return The category
222     */
223    protected CategoryProvider _getProvider (String categoryID)
224    {
225        Set<String> ids = _categoryProviderEP.getExtensionsIds();
226        for (String id : ids)
227        {
228            CategoryProvider provider = _categoryProviderEP.getExtension(id);
229            if (provider.hasCategory(categoryID))
230            {
231                return provider;
232            }
233        }
234        
235        return null;
236    }
237
238    @Override
239    public FunctionType getFunctionExecType()
240    {
241        return FunctionType.POST;
242    }
243
244    @Override
245    public I18nizableText getLabel()
246    {
247        return new I18nizableText("plugin.newsletter", "PLUGINS_NEWSLETTER_SEND_NEWSLETTER_FUNCTION_LABEL");
248    }
249}