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