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