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.cms.workflow;
017
018import java.util.ArrayList;
019import java.util.HashSet;
020import java.util.Iterator;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024
025import javax.mail.MessagingException;
026
027import org.apache.avalon.framework.activity.Initializable;
028import org.apache.commons.lang.StringUtils;
029import org.apache.excalibur.source.SourceResolver;
030
031import org.ametys.cms.repository.WorkflowAwareContent;
032import org.ametys.core.right.RightManager;
033import org.ametys.core.user.CurrentUserProvider;
034import org.ametys.core.user.User;
035import org.ametys.core.user.UserIdentity;
036import org.ametys.core.user.UserManager;
037import org.ametys.core.util.I18nUtils;
038import org.ametys.core.util.mail.SendMailHelper;
039import org.ametys.plugins.workflow.support.WorkflowProvider;
040import org.ametys.runtime.config.Config;
041import org.ametys.runtime.i18n.I18nizableText;
042import org.ametys.runtime.plugin.component.PluginAware;
043
044import com.opensymphony.module.propertyset.PropertySet;
045import com.opensymphony.workflow.FunctionProvider;
046import com.opensymphony.workflow.WorkflowException;
047
048/**
049 * OS workflow function to send mail after an action is triggered.
050 */
051public class SendMailFunction extends AbstractContentWorkflowComponent implements FunctionProvider, Initializable, PluginAware
052{
053    /**
054     * Provide "false" to prevent the function sending the mail.
055     * Useful when making large automatic workflow operations (for instance, when bulk importing and proposing in one action). 
056     */
057    public static final String SEND_MAIL = "send-mail";
058    
059    /** The rights key. */
060    protected static final String RIGHTS_KEY = "rights";
061    /** The mail subject key. */
062    protected static final String SUBJECT_KEY = "subjectKey";
063    /** The mail body key. */
064    protected static final String BODY_KEY = "bodyKey";
065    
066    /** The current user provider. */
067    protected CurrentUserProvider _currentUserProvider;
068    
069    /** The rights manager. */
070    protected RightManager _rightManager;
071    
072    /** The users manager. */
073    protected UserManager _userManager;
074    
075    /** The source resolver. */
076    protected SourceResolver _sourceResolver;
077    
078    /** The workflow. */
079    protected WorkflowProvider _workflowProvider;
080    
081    /** The plugin name. */
082    protected String _pluginName;
083    
084    /** I18nUtils */
085    protected I18nUtils _i18nUtils;
086    
087    @Override
088    public void initialize() throws Exception
089    {
090        _currentUserProvider = (CurrentUserProvider) _manager.lookup(CurrentUserProvider.ROLE);
091        _rightManager = (RightManager) _manager.lookup(RightManager.ROLE);
092        _userManager = (UserManager) _manager.lookup(UserManager.ROLE);
093        _sourceResolver = (SourceResolver) _manager.lookup(SourceResolver.ROLE);
094        _workflowProvider = (WorkflowProvider) _manager.lookup(WorkflowProvider.ROLE);
095        _i18nUtils = (I18nUtils) _manager.lookup(I18nUtils.ROLE);
096    }
097    
098    @Override
099    public void setPluginInfo(String pluginName, String featureName, String id)
100    {
101        _pluginName = pluginName;
102    }
103    
104    @Override
105    public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException
106    {
107        String rightsParam = StringUtils.defaultString((String) args.get(RIGHTS_KEY));
108        String subjectI18nKey = StringUtils.defaultString((String) args.get(SUBJECT_KEY));
109        String bodyI18nKey = StringUtils.defaultString((String) args.get(BODY_KEY));
110        
111        Set<String> rights = new HashSet<>();
112        for (String right : rightsParam.split(","))
113        {
114            if (StringUtils.isNotBlank(right))
115            {
116                rights.add(right.trim());
117            }
118        }
119        
120        // If "send-mail" is set to true or is not present in the vars, send the mail.
121        boolean dontSendMail = "false".equals(transientVars.get(SEND_MAIL));
122        
123        if (dontSendMail)
124        {
125            return;
126        }
127        
128        try
129        {
130            WorkflowAwareContent content = getContent(transientVars);
131            UserIdentity userIdentity = getUser(transientVars);
132            User user = _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin());
133            
134            Set<UserIdentity> users = _getUsers(content, rights);
135            
136            String mailSubject = getMailSubject(subjectI18nKey, user, content);
137            String mailBody = getMailBody(bodyI18nKey, user, content, transientVars);
138            
139            _sendMails(mailSubject, mailBody, users, user.getEmail());
140        }
141        catch (Exception e)
142        {
143            _logger.error("An error occurred: unable to send mail to notify workflow change.", e);
144        }
145    }
146    
147    /**
148     * Get the subject of mail
149     * @param subjectI18nKey  the i18n key to use for subject
150     * @param user the caller
151     * @param content the content
152     * @return the subject
153     */
154    protected String getMailSubject (String subjectI18nKey, User user, WorkflowAwareContent content)
155    {
156        I18nizableText subjectKey = new I18nizableText(null, subjectI18nKey, getSubjectI18nParams(user, content));
157        return _i18nUtils.translate(subjectKey, null); // FIXME Use user preference language
158    }
159    
160    /**
161     * Get the text body of mail
162     * @param bodyI18nKey the i18n key to use for body
163     * @param user the caller
164     * @param content the content
165     * @param transientVars the transient variables
166     * @return the text body
167     */
168    protected String getMailBody (String bodyI18nKey, User user, WorkflowAwareContent content, Map transientVars)
169    {
170        I18nizableText bodyKey = new I18nizableText(null, bodyI18nKey, getBodyI18nParams(user, content));
171        String mailBody = _i18nUtils.translate(bodyKey, null); // FIXME Use user preference language
172        
173        // Get the workflow comment
174        String comment = (String) transientVars.get("comment");
175        if (StringUtils.isNotEmpty(comment))
176        {
177            List<String> params = new ArrayList<>();
178            params.add(comment);
179            
180            I18nizableText commentKey = new I18nizableText("plugin.cms", "WORKFLOW_MAIL_BODY_USER_COMMENT", params);
181            String commentTxt = _i18nUtils.translate(commentKey, null); // FIXME Use user preference language
182            
183            mailBody += "\n\n" + commentTxt; 
184        }
185        
186        return mailBody;
187    }
188    
189    /**
190     * Send the notification emails.
191     * @param subject the e-mail subject.
192     * @param body the e-mail body.
193     * @param users users to send the mail to.
194     * @param from the address sending the e-mail.
195     */
196    protected void _sendMails(String subject, String body, Set<UserIdentity> users, String from)
197    {
198        for (UserIdentity userIdentity : users)
199        {
200            User user = _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin());
201            if (user != null && StringUtils.isNotBlank(user.getEmail()))
202            {
203                String mail = user.getEmail();
204                
205                try
206                {
207                    SendMailHelper.sendMail(subject, null, body, mail, from);
208                }
209                catch (MessagingException e)
210                {
211                    _logger.warn("Could not send a workflow notification mail to " + mail, e);
212                }
213            }
214        }
215    }
216    
217    /**
218     * Get the i18n parameters of mail subject
219     * @param user the caller
220     * @param content the content
221     * @return the i18n parameters
222     */
223    protected List<String> getSubjectI18nParams (User user, WorkflowAwareContent content)
224    {
225        List<String> params = new ArrayList<>();
226        params.add(content.getTitle());
227        return params;
228    }
229    
230    /**
231     * Get the i18n parameters of mail body text
232     * @param user the caller
233     * @param content the content
234     * @return the i18n parameters
235     */
236    protected List<String> getBodyI18nParams (User user, WorkflowAwareContent content)
237    {
238        List<String> params = new ArrayList<>();
239        
240        params.add(user.getFullName()); // {0}
241        params.add(content.getTitle()); // {1}
242        
243        String requestUri = StringUtils.stripEnd(StringUtils.removeEndIgnoreCase(Config.getInstance().getValueAsString("cms.url"), "index.html"), "/");
244        params.add(requestUri + "/index.html?uitool=uitool-content,id:%27" + content.getId() + "%27"); // {2}
245        
246        return params;
247    }
248    
249    /**
250     * Get the user logins.
251     * @param content the content.
252     * @param rights the set of rights to check.
253     * @return the users.
254     * @throws WorkflowException If an error occurred
255     */
256    protected Set<UserIdentity> _getUsers(WorkflowAwareContent content, Set<String> rights) throws WorkflowException
257    {
258        Set<UserIdentity> users = new HashSet<>();
259        
260        Iterator<String> rightIt = rights.iterator();
261        
262        // First right : add all the granted users.
263        if (rightIt.hasNext())
264        {
265            users.addAll(_rightManager.getAllowedUsers(rightIt.next(), content).resolveAllowedUsers(Config.getInstance().getValueAsBoolean("runtime.mail.massive.sending")));
266        }
267        
268        // Next rights : retain all the granted users.
269        while (rightIt.hasNext())
270        {
271            users.retainAll(_rightManager.getAllowedUsers(rightIt.next(), content).resolveAllowedUsers(Config.getInstance().getValueAsBoolean("runtime.mail.massive.sending")));
272        }
273        
274        return users;
275    }
276}