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.web.alerts;
017
018import java.util.ArrayList;
019import java.util.Calendar;
020import java.util.GregorianCalendar;
021import java.util.HashSet;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Set;
025
026import org.apache.avalon.framework.configuration.Configuration;
027import org.apache.avalon.framework.configuration.ConfigurationException;
028import org.apache.cocoon.components.ContextHelper;
029import org.apache.cocoon.environment.Request;
030
031import org.ametys.cms.repository.Content;
032import org.ametys.core.user.UserIdentity;
033import org.ametys.core.user.population.PopulationContextHelper;
034import org.ametys.plugins.repository.AmetysObjectIterable;
035import org.ametys.plugins.repository.AmetysRepositoryException;
036import org.ametys.plugins.repository.query.expression.DateExpression;
037import org.ametys.plugins.repository.query.expression.Expression;
038import org.ametys.plugins.repository.query.expression.Expression.Operator;
039import org.ametys.runtime.config.Config;
040import org.ametys.runtime.i18n.I18nizableText;
041import org.ametys.web.repository.content.WebContent;
042import org.ametys.web.repository.page.Page;
043import org.ametys.web.repository.page.PageQueryHelper;
044import org.ametys.web.repository.page.jcr.DefaultPage;
045
046/**
047 * Alerts engine: sends alerts mail.
048 * This is the web version of the engine: it sets the currently processed content's
049 * site name in the request object, and sends additional site and page information
050 * in the alerts e-mails.
051 */
052public class AlertEngine extends org.ametys.cms.alerts.AlertEngine
053{
054    /** True if the alert of page publication ending is enabled */
055    protected boolean _pagePublicationEnabled;
056    
057    /** The "page nearing end of publication" e-mail will be sent to users that have this right. */
058    protected Set<String> _pagePublicationEndRights;
059    
060    /** The "page nearing end of publication" e-mail subject i18n key. */
061    protected String _pagePublicationEndSubject;
062    
063    /** The "page nearing end of publication" e-mail body i18n key. */
064    protected String _pagePublicationEndBody;
065    
066    /**
067     * Default constructor
068     */
069    public AlertEngine()
070    {
071        super();
072    }
073    
074    /**
075     * Constructor used to send instant alerts
076     * @param contentIds The content's id
077     * @param message the message
078     */
079    public AlertEngine (List<String> contentIds, String message)
080    {
081        super(contentIds, message);
082    }
083    
084    @Override
085    public void configure(Configuration configuration) throws ConfigurationException
086    {
087        super.configure(configuration);
088        
089        Configuration pagePublicationEndConf = configuration.getChild("pagePublicationEnd", false);
090        
091        if (pagePublicationEndConf != null)
092        {
093            _pagePublicationEnabled = true;
094            _pagePublicationEndRights = _getRightsFromConf(pagePublicationEndConf);
095            _pagePublicationEndSubject = pagePublicationEndConf.getChild("subjectKey").getValue();
096            _pagePublicationEndBody = pagePublicationEndConf.getChild("bodyKey").getValue();
097        }
098    }
099    
100    @Override
101    protected void _sendAlerts() throws AmetysRepositoryException
102    {
103        // Send the content alerts.
104        super._sendAlerts();
105        
106        if (!_instantMode && _pagePublicationEnabled)
107        {
108            // Send the page alerts.
109            _sendPagePublicationEndAlerts();
110        }
111    }
112    
113    @Override
114    protected void _setRequestAttributes (Content content)
115    {
116        Request request = ContextHelper.getRequest(_context);
117        
118        if (content instanceof WebContent)
119        {
120            String siteName = ((WebContent) content).getSiteName();
121            
122            // Set the site name into the request for the other components to be able to retrieve it.
123            request.setAttribute("siteName", siteName);
124            
125            List<String> populationContexts = new ArrayList<>();
126            
127            // Set the population contexts to be able to get allowed users
128            populationContexts.add("/sites/" + siteName);
129            populationContexts.add("/sites-fo/" + siteName);
130            
131            request.setAttribute(PopulationContextHelper.POPULATION_CONTEXTS_REQUEST_ATTR, populationContexts);
132        }
133    }
134    
135    /**
136     * Set the necessary request attributes 
137     * @param page The concerned page
138     */
139    protected void _setRequestAttributes (Page page)
140    {
141        Request request = ContextHelper.getRequest(_context);
142        
143        String siteName = page.getSiteName();
144        
145        // Set the site name into the request for the other components to be able to retrieve it.
146        request.setAttribute("siteName", siteName);
147        
148        List<String> populationContexts = new ArrayList<>();
149        
150        // Set the population contexts to be able to get allowed users
151        populationContexts.add("/sites/" + siteName);
152        populationContexts.add("/sites-fo/" + siteName);
153        
154        request.setAttribute(PopulationContextHelper.POPULATION_CONTEXTS_REQUEST_ATTR, populationContexts);
155    }
156    
157    /**
158     * Send the "page nearing end of publication" alerts.
159     * @throws AmetysRepositoryException if an error occurs.
160     */
161    protected void _sendPagePublicationEndAlerts() throws AmetysRepositoryException
162    {
163        Long delay = Config.getInstance().getValueAsLong("remind.before.publication.end");
164        if (delay != null && delay > 0)
165        {
166            Calendar calendar = new GregorianCalendar();
167            _removeTimeParts(calendar);
168            calendar.add(Calendar.DAY_OF_MONTH, delay.intValue());
169            
170            // Get all the pages which publication end is in X days.
171            Expression nearPublicationEndExpr = new DateExpression(DefaultPage.METADATA_PUBLICATION_END_DATE, Operator.EQ, calendar.getTime());
172            
173            String query = PageQueryHelper.getPageXPathQuery(null, null, null, nearPublicationEndExpr, null);
174            
175            try (AmetysObjectIterable<Page> pages = _ametysResolver.query(query))
176            {
177                if (_LOGGER.isInfoEnabled())
178                {
179                    _LOGGER.info("Pages within " + Long.toString(delay) + " days of publication end: " + pages.getSize());
180                }
181                
182                for (Page page : pages)
183                {
184                    // Send the alert e-mails.
185                    _sendPagePublicationEndEmail(page);
186                }
187            }
188        }
189    }
190    
191    /**
192     * Send the "page publication end" alert e-mail.
193     * @param page the page nearing publication end.
194     * @throws AmetysRepositoryException if an error occurs.
195     */
196    protected void _sendPagePublicationEndEmail(Page page) throws AmetysRepositoryException
197    {
198        _setRequestAttributes(page);
199        
200        Set<UserIdentity> users = new HashSet<>();
201        for (String right : _pagePublicationEndRights)
202        {
203            users.addAll(_rightManager.getAllowedUsers(right, page).resolveAllowedUsers(Config.getInstance().getValueAsBoolean("runtime.mail.massive.sending")));
204        }
205        
206        List<String> params = _getPagePublicationEndParams(page);
207        
208        I18nizableText i18nSubject = new I18nizableText(null, _pagePublicationEndSubject, params);
209        I18nizableText i18nBody = new I18nizableText(null, _pagePublicationEndBody, params);
210        
211        String subject = _i18nUtils.translate(i18nSubject);
212        String body = _i18nUtils.translate(i18nBody);
213        
214        _sendMails(subject, body, users, _mailFrom);
215    }
216    
217    @Override
218    protected String getI18nKeyBody(String bodyI18nKey, Content content)
219    {
220        String i18nKey = bodyI18nKey;
221        
222        if (content instanceof WebContent)
223        {
224            WebContent webContent = (WebContent) content;
225            if (webContent.getReferencingPages().size() == 0)
226            {
227                // Orphan content
228                i18nKey += "_ORPHAN";
229            }
230        }
231        else
232        {
233            i18nKey += "_NOSITE";
234        }
235        
236        return i18nKey;
237    }
238    
239    @Override
240    protected List<String> _getAdditionalParams(Content content)
241    {
242        List<String> params = new ArrayList<>();
243        
244        if (content instanceof WebContent)
245        {
246            WebContent webContent = (WebContent) content;
247            
248            params.add(webContent.getSite().getTitle());  // {4}
249            
250            Iterator<Page> pages = webContent.getReferencingPages().iterator();
251            if (pages.hasNext())
252            {
253                Page page = pages.next();
254                params.add(page.getTitle()); // {5}
255            }
256        }
257        
258        return params;
259    }
260    
261    /**
262     * Get the "page nearing end of publication" mail parameters.
263     * @param page the page.
264     * @return the mail parameters.
265     */
266    protected List<String> _getPagePublicationEndParams(Page page)
267    {
268        List<String> params = new ArrayList<>();
269        
270        String siteTitle = page.getSite().getTitle();
271        String delay = Config.getInstance().getValueAsString("remind.before.publication.end");
272        
273        params.add(page.getTitle());
274        params.add(_getPageUrl(page));
275        params.add(delay);
276        params.add(siteTitle);
277        
278        return params;
279    }
280    
281    @Override
282    protected String _getContentUrl(Content content)
283    {
284        if (content instanceof WebContent)
285        {
286            WebContent webContent = (WebContent) content;
287            
288            StringBuilder url = new StringBuilder(_baseUrl);
289            if (!_baseUrl.endsWith("/"))
290            {
291                url.append('/');
292            }
293            
294            Iterator<Page> pages = webContent.getReferencingPages().iterator();
295            if (pages.hasNext())
296            {
297                Page page = pages.next();
298                url.append(webContent.getSite().getName()).append("/index.html?uitool=uitool-page,id:%27").append(page.getId()).append("%27");
299            }
300            else
301            {
302                url.append(webContent.getSite().getName()).append("/index.html?uitool=uitool-content,id:%27").append(content.getId()).append("%27");
303            }
304            
305            return url.toString();
306        }
307        else
308        {
309            return super._getContentUrl(content);
310        }
311    }
312    
313    /**
314     * Get the URL of the back-office, opening on the page tool.
315     * @param page the page to link to.
316     * @return the page URL.
317     */
318    protected String _getPageUrl(Page page)
319    {
320        StringBuilder url = new StringBuilder(_baseUrl);
321        if (!_baseUrl.endsWith("/"))
322        {
323            url.append('/');
324        }
325        url.append(page.getSite().getName()).append("/index.html?uitool=uitool-page,id:%27").append(page.getId()).append("%27");
326        return url.toString();
327    }
328    
329}