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 */
016
017package org.ametys.web.repository.comment;
018
019import java.io.IOException;
020import java.util.ArrayList;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024
025import org.apache.avalon.framework.context.Context;
026import org.apache.avalon.framework.context.ContextException;
027import org.apache.avalon.framework.context.Contextualizable;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.avalon.framework.service.Serviceable;
031import org.apache.cocoon.components.ContextHelper;
032import org.apache.cocoon.environment.Request;
033import org.apache.commons.lang.StringUtils;
034
035import org.ametys.cms.ObservationConstants;
036import org.ametys.cms.content.ContentHelper;
037import org.ametys.cms.repository.Content;
038import org.ametys.cms.repository.comment.Comment;
039import org.ametys.core.observation.Event;
040import org.ametys.core.observation.Observer;
041import org.ametys.core.right.RightManager;
042import org.ametys.core.user.User;
043import org.ametys.core.user.UserIdentity;
044import org.ametys.core.user.UserManager;
045import org.ametys.core.user.population.PopulationContextHelper;
046import org.ametys.core.util.I18nUtils;
047import org.ametys.core.util.mail.SendMailHelper;
048import org.ametys.runtime.config.Config;
049import org.ametys.runtime.i18n.I18nizableText;
050import org.ametys.runtime.plugin.component.AbstractLogEnabled;
051import org.ametys.runtime.plugin.component.PluginAware;
052import org.ametys.web.repository.content.WebContent;
053import org.ametys.web.repository.site.Site;
054
055import jakarta.mail.MessagingException;
056
057/**
058 * Listener to send mails to moderators or observers
059 */
060public class SendMailToContributorCommentListener extends AbstractLogEnabled implements Observer, Serviceable, Contextualizable, PluginAware
061{
062    /** The i18n utils of runtime */
063    protected I18nUtils _i18nUtils;
064    /** The avalon context */
065    protected Context _context;
066    /** The ametys rights manager */
067    protected RightManager _rightManager;
068    /** The users manager. */
069    protected UserManager _userManager;
070    /** The content helper */
071    protected ContentHelper _contentHelper;
072    /** The plugin name */
073    protected String _pluginName;
074    
075    @Override
076    public void service(ServiceManager manager) throws ServiceException
077    {
078        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
079        _rightManager = (RightManager) manager.lookup(RightManager.ROLE);
080        _userManager = (UserManager) manager.lookup(UserManager.ROLE);
081        _contentHelper = (ContentHelper) manager.lookup(ContentHelper.ROLE);
082    }
083    
084    @Override
085    public void contextualize(Context context) throws ContextException
086    {
087        _context = context;
088    }
089    
090    public void setPluginInfo(String pluginName, String featureName, String id)
091    {
092        _pluginName = pluginName;
093    }
094    
095    public int getPriority(Event event)
096    {
097        return MAX_PRIORITY;
098    }
099    
100    @Override
101    public boolean supports(Event event)
102    {
103        return event.getId().equals(ObservationConstants.EVENT_CONTENT_COMMENT_VALIDATED) 
104            || event.getId().equals(ObservationConstants.EVENT_CONTENT_COMMENT_REPORTED)
105            || event.getId().equals(ObservationConstants.EVENT_CONTENT_COMMENT_ADDED);
106    }
107    
108    public void observe(Event event, Map<String, Object> transientVars) throws Exception
109    {
110        Map<String, Object> arguments = event.getArguments();
111        Content content = (Content) arguments.get(ObservationConstants.ARGS_CONTENT);
112        Comment comment = (Comment) arguments.get(ObservationConstants.ARGS_COMMENT);
113        
114        _setRequestAttributes (content);
115        
116        if (event.getId().equals(ObservationConstants.EVENT_CONTENT_COMMENT_ADDED))
117        {
118            _sendCommentAddedNotificationMail(content, comment);
119        }
120        else if (event.getId().equals(ObservationConstants.EVENT_CONTENT_COMMENT_REPORTED))
121        {
122            UserIdentity issuer = event.getIssuer();
123            _sendCommentReportedNotificationMail(content, comment, issuer);
124        }
125        else
126        {
127            _sendCommentValidatedNotificationMail(content, comment);
128        }
129    }
130    
131    private void _setRequestAttributes (Content content)
132    {
133        Request request = ContextHelper.getRequest(_context);
134        
135        if (content instanceof WebContent)
136        {
137            String siteName = ((WebContent) content).getSiteName();
138            
139            // Set the site name into the request for the other components to be able to retrieve it.
140            request.setAttribute("siteName", siteName);
141            
142            List<String> populationContexts = new ArrayList<>();
143            
144            // Set the population contexts to be able to get allowed users
145            populationContexts.add("/sites/" + siteName);
146            populationContexts.add("/sites-fo/" + siteName);
147            
148            request.setAttribute(PopulationContextHelper.POPULATION_CONTEXTS_REQUEST_ATTR, populationContexts);
149        }
150    }
151    
152    private List<String> _getRecipients(Set<UserIdentity> users)
153    {
154        List<String> recipients = new ArrayList<>();
155        
156        for (UserIdentity userIdentity : users)
157        {
158            User user = _userManager.getUser(userIdentity);
159            if (user != null && StringUtils.isNotBlank(user.getEmail()))
160            {
161                recipients.add(user.getEmail());
162            }
163        }
164        return recipients;
165    }
166    
167    /**
168     * Send email notification on comment added
169     * @param content the content
170     * @param comment the added comment
171     */
172    protected void _sendCommentAddedNotificationMail (Content content, Comment comment)
173    {
174        if (comment.isValidated())
175        {
176            Set<UserIdentity> users = _rightManager.getAllowedUsers("CMS_Rights_CommentNotified", content).resolveAllowedUsers(Config.getInstance().getValue("runtime.mail.massive.sending"));
177            List<String> recipients = _getRecipients(users);
178            
179            _sendMail(content, comment, recipients, "PLUGINS_WEB_CONTENT_COMMENTS_NOTIFICATION_SUBJECT", "PLUGINS_WEB_CONTENT_COMMENTS_NOTIFICATION_BODY");
180        }
181        else
182        {
183            // Determine the list of listeners
184            Set<UserIdentity> users = _rightManager.getAllowedUsers("CMS_Rights_CommentModerate", content).resolveAllowedUsers(Config.getInstance().getValue("runtime.mail.massive.sending"));
185            List<String> recipients = _getRecipients(users);
186            
187            _sendMail(content, comment, recipients, "PLUGINS_WEB_CONTENT_COMMENTS_MODERATION_SUBJECT", "PLUGINS_WEB_CONTENT_COMMENTS_MODERATION_BODY");
188        }
189    }
190    
191    /**
192     * Send email notification on comment validated
193     * @param content the content
194     * @param comment the validated comment
195     */
196    protected void _sendCommentValidatedNotificationMail (Content content, Comment comment)
197    {
198        Set<UserIdentity> users = _rightManager.getAllowedUsers("CMS_Rights_CommentNotified", content).resolveAllowedUsers(Config.getInstance().getValue("runtime.mail.massive.sending"));
199        List<String> recipients = _getRecipients(users);
200        
201        _sendMail(content, comment, recipients, "PLUGINS_WEB_CONTENT_COMMENTS_NOTIFICATION_SUBJECT", "PLUGINS_WEB_CONTENT_COMMENTS_NOTIFICATION_BODY");
202    }
203    
204    /**
205     * Send email notification on comment report
206     * @param content the content
207     * @param comment the reported comment
208     * @param issuer the issuer
209     */
210    protected void _sendCommentReportedNotificationMail (Content content, Comment comment, UserIdentity issuer)
211    {
212        Set<UserIdentity> users = _rightManager.getAllowedUsers("CMS_Rights_CommentNotified", content).resolveAllowedUsers(Config.getInstance().getValue("runtime.mail.massive.sending"));
213        List<String> recipients = _getRecipients(users);
214        
215        List<String> i18nParams = _getCommonBodyI18nParams(content, comment);
216        User issuerAsUser = _userManager.getUser(issuer);
217        i18nParams.add(issuerAsUser.getFullName()); // {6}
218        
219        _sendMail(content, comment, recipients, "PLUGINS_WEB_CONTENT_COMMENTS_REPORTED_SUBJECT", "PLUGINS_WEB_CONTENT_COMMENTS_REPORTED_BODY");
220    }
221    
222    /**
223     * Send notification on comment to contributor
224     * @param content the content
225     * @param comment the modified comment
226     * @param recipients the list of recipients
227     * @param subjectI18nKey the i18n for email subject
228     * @param bodyI18nKey the i18n for email body
229     */
230    protected void _sendMail(Content content, Comment comment, List<String> recipients, String subjectI18nKey, String bodyI18nKey)
231    {
232        _sendMail(content, comment, recipients, subjectI18nKey, bodyI18nKey, _getCommonBodyI18nParams(content, comment));
233    }
234    
235    /**
236     * Send notification on comment to contributor
237     * @param content the content
238     * @param comment the modified comment
239     * @param recipients the list of recipients
240     * @param subjectI18nKey the i18n for email subject
241     * @param bodyI18nKey the i18n for email body
242     * @param i18nParams the i18n parameters
243     */
244    protected void _sendMail(Content content, Comment comment, List<String> recipients, String subjectI18nKey, String bodyI18nKey, List<String> i18nParams)
245    {
246        getLogger().debug("Send email notification to '{}' for his comment '{}' on content '{}'", comment.getAuthorEmail(), comment.getId(), content.getId());
247
248        I18nizableText i18nSubject = new I18nizableText("plugin." + _pluginName, subjectI18nKey, i18nParams);
249        String subject = _i18nUtils.translate(i18nSubject, content.getLanguage());
250        
251        I18nizableText i18nBody = new I18nizableText("plugin." + _pluginName, bodyI18nKey, i18nParams);
252        String body = _i18nUtils.translate(i18nBody, content.getLanguage());
253        
254        String from = Config.getInstance().getValue("smtp.mail.from");
255        if (content instanceof WebContent)
256        {
257            Site site = ((WebContent) content).getSite();
258            from = site.getValue("site-mail-from");
259        }
260        
261        try
262        {
263            SendMailHelper.newMail()
264                          .withSubject(subject)
265                          .withTextBody(body)
266                          .withSender(from)
267                          .withRecipients(recipients)
268                          .withAsync(true)
269                          .sendMail();
270        }
271        catch (MessagingException | IOException e)
272        {
273            getLogger().warn("Could not send a notification mail to {}", recipients, e);
274        }  
275    }
276    
277    /**
278     * The comment i18n parameters for email notification
279     * @param content the content
280     * @param comment the comment
281     * @return the i18n parameters
282     */
283    protected List<String> _getCommonBodyI18nParams(Content content, Comment comment)
284    {
285        List<String> i18nparam = new ArrayList<>();
286        
287        i18nparam.add(_contentHelper.getTitle(content)); // {0} 
288        i18nparam.add(_getContentURI(content));  // {1} 
289        i18nparam.add(comment.getAuthorName());  // {2} 
290        i18nparam.add(comment.getContent());  // {3} 
291        
292        if (content instanceof WebContent)
293        {
294            Site site = ((WebContent) content).getSite();
295            i18nparam.add(site.getName()); // {4}
296            i18nparam.add(site.getTitle()); // {5}
297        }
298        
299        return i18nparam;
300    }
301    
302    /**
303     * Get the content URI in back-office
304     * @param content the content
305     * @return the content uri
306     */
307    protected String _getContentURI(Content content)
308    {
309        String cmsUrl = StringUtils.stripEnd(StringUtils.removeEndIgnoreCase(Config.getInstance().getValue("cms.url"), "index.html"), "/");
310        
311        StringBuilder url = new StringBuilder(cmsUrl);
312        
313        if (content instanceof WebContent)
314        {
315            Site site = ((WebContent) content).getSite();
316            url.append("/").append(site.getName());
317        }
318        url.append("/index.html?uitool=uitool-content,id:%27").append(content.getId()).append("%27");
319        return url.toString();
320    }
321}