001/*
002 *  Copyright 2020 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.cms.schedule;
017
018import java.io.IOException;
019import java.util.Optional;
020
021import org.apache.avalon.framework.activity.Initializable;
022import org.apache.avalon.framework.service.ServiceException;
023import org.apache.avalon.framework.service.ServiceManager;
024import org.apache.commons.lang3.StringUtils;
025import org.quartz.JobExecutionContext;
026
027import org.ametys.core.user.CurrentUserProvider;
028import org.ametys.core.user.User;
029import org.ametys.core.util.I18nUtils;
030import org.ametys.core.util.mail.SendMailHelper;
031import org.ametys.core.util.mail.SendMailHelper.MailBuilder;
032import org.ametys.plugins.core.impl.schedule.AbstractStaticSchedulable;
033import org.ametys.plugins.core.user.UserHelper;
034import org.ametys.runtime.config.Config;
035import org.ametys.runtime.i18n.I18nizableText;
036
037import jakarta.mail.MessagingException;
038
039/**
040 * Abstract schedulable that send an email at the end of the execution
041 * By default, the email is sent to the user that launched the schedulable. This behavior can be overridden thanks to the {@link #_getRecipient(JobExecutionContext)} method
042 * If this user has no email address and there is an error during the execution, an email is sent to the system administrator
043 */
044public abstract class AbstractSendingMailSchedulable extends AbstractStaticSchedulable implements Initializable
045{
046    /** The utils for i18n */
047    protected I18nUtils _i18nUtils;
048    /** Mail sender */
049    protected String _mailSender;
050    /** Sys admin mail */
051    protected String _sysadminMail;
052    /** Current user provider */
053    protected CurrentUserProvider _currentUserProvider;
054    /** User helper */
055    protected UserHelper _userHelper;
056    
057    @Override
058    public void service(ServiceManager manager) throws ServiceException
059    {
060        super.service(manager);
061        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
062        _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE);
063        _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE);
064    }
065
066    public void initialize() throws Exception
067    {
068        _mailSender = Config.getInstance().getValue("smtp.mail.from");
069        _sysadminMail = Config.getInstance().getValue("smtp.mail.sysadminto");
070    }
071
072    @Override
073    public void execute(JobExecutionContext context) throws Exception
074    {
075        Optional<String> recipient = _getRecipient(context);
076        
077        I18nizableText mailSubject = null;
078        I18nizableText mailBody = null;
079        try
080        {
081            _doExecute(context);
082            
083            mailSubject = _getSuccessMailSubject(context);
084            mailBody = _getSuccessMailBody(context);
085        }
086        catch (Exception e)
087        {
088            if (recipient.isEmpty())
089            {
090                recipient = Optional.ofNullable(_sysadminMail)
091                        .filter(StringUtils::isNotEmpty);
092            }
093            
094            mailSubject = _getErrorMailSubject(context);
095            mailBody = _getErrorMailBody(context, e);
096
097            throw e;
098        }
099        finally
100        {
101            if (recipient.isPresent())
102            {
103                _sendMail(mailSubject, mailBody, recipient.get(), context);
104            }
105        }
106    }
107    
108    /**
109     * Executes the schedulable.
110     * @param context the context
111     * @throws Exception if an error occurred
112     */
113    protected abstract void _doExecute(JobExecutionContext context) throws Exception;
114    
115    /**
116     * Retrieves the optional recipient of the mail
117     * @param context the context
118     * @return the optional recipient of the mail
119     */
120    protected Optional<String> _getRecipient(JobExecutionContext context)
121    {
122        return Optional.of(_currentUserProvider)
123            .map(CurrentUserProvider::getUser)
124            .map(_userManager::getUser)
125            .map(User::getEmail)
126            .filter(StringUtils::isNotEmpty);
127    }
128    
129    /**
130     * Determines if the mail body is in HTML
131     * @param context the context
132     * @return <code>true</code> if the mail body is in HTML, <code>false</code> otherwise
133     * @throws Exception If an error occurs while retrieving if mail body should be HTML
134     */
135    protected boolean _isMailBodyInHTML(JobExecutionContext context) throws Exception
136    {
137        return false;
138    }
139    
140    /**
141     * Retrieves the subject of the success mail
142     * @param context the context
143     * @return the subject of the success mail
144     * @throws Exception If an error occurs while building the mail subject
145     */
146    protected abstract I18nizableText _getSuccessMailSubject(JobExecutionContext context) throws Exception;
147    
148    /**
149     * Retrieves the body of the success mail
150     * @param context the context
151     * @return the body of the success mail
152     * @throws Exception If an error occurs while building the mail body
153     */
154    protected abstract I18nizableText _getSuccessMailBody(JobExecutionContext context) throws Exception;
155    
156    /**
157     * Retrieves the subject of the error mail
158     * @param context the context
159     * @return the subject of the error mail
160     * @throws Exception If an error occurs while building the mail subject
161     */
162    protected abstract I18nizableText _getErrorMailSubject(JobExecutionContext context) throws Exception;
163    
164    /**
165     * Retrieves the body of the error mail
166     * @param context the context
167     * @param throwable the error
168     * @return the body of the error mail
169     * @throws Exception If an error occurs while building the mail body
170     */
171    protected abstract I18nizableText _getErrorMailBody(JobExecutionContext context, Throwable throwable) throws Exception;
172    
173    /**
174     * Send an email
175     * @param subject the email's subject
176     * @param body the email's body
177     * @param recipient the recipient address
178     * @param context the context
179     */
180    protected void _sendMail (I18nizableText subject, I18nizableText body, String recipient, JobExecutionContext context)
181    {
182        try
183        {
184            MailBuilder mailBuilder = SendMailHelper.newMail()
185                                                    .withSubject(_i18nUtils.translate(subject))
186                                                    .withRecipient(recipient)
187                                                    .withSender(_mailSender);
188            
189            if (_isMailBodyInHTML(context))
190            {
191                mailBuilder.withHTMLBody(_i18nUtils.translate(body));
192            }
193            else
194            {
195                mailBuilder.withTextBody(_i18nUtils.translate(body));
196            }
197            
198            mailBuilder.sendMail();
199        }
200        catch (MessagingException | IOException e)
201        {
202            if (getLogger().isWarnEnabled())
203            {
204                getLogger().warn("Unable to send the e-mail '" + subject  + "' to '" + recipient + "'", e);
205            }
206        }
207        catch (Exception e)
208        {
209            getLogger().error("An unknown error has occured while sending the mail.", e);
210        }
211    }
212}