001/*
002 *  Copyright 2026 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.repository.comment;
017
018import java.util.ArrayList;
019import java.util.List;
020import java.util.Objects;
021import java.util.Optional;
022import java.util.Set;
023
024import org.apache.avalon.framework.context.Context;
025import org.apache.avalon.framework.context.ContextException;
026import org.apache.avalon.framework.context.Contextualizable;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030import org.apache.cocoon.components.ContextHelper;
031import org.apache.cocoon.environment.Request;
032import org.apache.commons.lang3.StringUtils;
033
034import org.ametys.cms.content.ContentHelper;
035import org.ametys.cms.repository.Content;
036import org.ametys.cms.repository.comment.Comment;
037import org.ametys.core.observation.Observer;
038import org.ametys.core.user.User;
039import org.ametys.core.user.UserIdentity;
040import org.ametys.core.user.UserManager;
041import org.ametys.core.user.directory.NotUniqueUserException;
042import org.ametys.core.user.population.PopulationContextHelper;
043import org.ametys.core.util.I18nUtils;
044import org.ametys.core.util.language.UserLanguagesManager;
045import org.ametys.runtime.config.Config;
046import org.ametys.runtime.plugin.component.AbstractLogEnabled;
047import org.ametys.runtime.plugin.component.PluginAware;
048import org.ametys.web.WebHelper;
049import org.ametys.web.repository.content.WebContent;
050import org.ametys.web.repository.site.Site;
051import org.ametys.web.repository.site.SiteManager;
052
053/**
054 * Abstract observer to notify comment authors when a comment is created or updated.
055 */
056public abstract class AbstractNotifyCommentAuthorObserver extends AbstractLogEnabled implements Observer, Serviceable, PluginAware, Contextualizable
057{
058    /** The i18n utils of runtime */
059    protected I18nUtils _i18nUtils;
060    
061    /** The content helper */
062    protected ContentHelper _contentHelper;
063
064    /** The plugin */
065    protected String _pluginName;
066
067    /** The DAO for comments */
068    protected CommentsDAO _commentsDAO;
069
070    /** The user manager */
071    protected UserManager _userManager;
072
073    /** The user languages manager */
074    protected UserLanguagesManager _userLanguagesManager;
075    /** The population context helper */
076    protected PopulationContextHelper _populationContextHelper;
077    /** The site manager */
078    protected SiteManager _siteManager;
079
080    /** The avalon context */
081    protected Context _context;
082    
083    @Override
084    public void service(ServiceManager manager) throws ServiceException
085    {
086        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
087        _contentHelper = (ContentHelper) manager.lookup(ContentHelper.ROLE);
088        _commentsDAO = (CommentsDAO) manager.lookup(org.ametys.cms.repository.comment.CommentsDAO.ROLE);
089        _userManager = (UserManager) manager.lookup(UserManager.ROLE);
090        _userLanguagesManager = (UserLanguagesManager) manager.lookup(UserLanguagesManager.ROLE);
091        _populationContextHelper = (PopulationContextHelper) manager.lookup(PopulationContextHelper.ROLE);
092        _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE);
093    }
094    
095    public void contextualize(Context context) throws ContextException
096    {
097        _context = context;
098    }
099    
100    public void setPluginInfo(String pluginName, String featureName, String id)
101    {
102        _pluginName = pluginName;
103    }
104    
105    public int getPriority()
106    {
107        return MAX_PRIORITY;
108    }
109    
110    /**
111     * Get the language to use for the mail notification of a comment
112     * @param content The content commented
113     * @param comment The comment it self
114     * @return The language to use for the mail notification of a comment
115     */
116    protected String getMailLanguage(Content content, Comment comment)
117    {
118        return getMailLanguage(content, comment.getAuthor(), comment.getAuthorEmail());
119    }
120    
121    /**
122     * Get the language to use for the mail notification of a comment
123     * @param content The content commented
124     * @param commentAuthor The user identity of the comment's author if it exists (null for unauthenticated comment), used to get the user's language
125     * @param commentAuthorEmail The email of the comment's author, used to find a user with this email on the content's site and get its language if the author user identity is not provided
126     * @return The language to use for the mail notification of a comment
127     */
128    protected String getMailLanguage(Content content, UserIdentity commentAuthor, String commentAuthorEmail)
129    {
130        // If an author user identity is provided from the comment, use its language
131        User authorUser = resolveCommentAuthor(content, commentAuthor, commentAuthorEmail);
132        return getMailLanguage(content, authorUser);
133    }
134    
135    /**
136     * Get the language to use for the mail notification of a comment
137     * @param content The content commented
138     * @param user The user to notify. Can be null, in this case the content's language or the default language will be used
139     * @return The language to use for the mail notification of a comment
140     */
141    protected String getMailLanguage(Content content, User user)
142    {
143        String userLanguage = null;
144        if (user != null)
145        {
146            userLanguage = user.getLanguage();
147        }
148        
149        // If no language was found, use the content's language then the default language
150        return StringUtils.defaultIfBlank(userLanguage, StringUtils.defaultIfBlank(content.getLanguage(), _userLanguagesManager.getDefaultLanguage()));
151    }
152    
153    /**
154     * Try to resolve the user author of the comment as a User from author or email provided in the comment
155     * @param content the commented content
156     * @param comment the comment
157     * @return The user author or null if not found
158     */
159    protected User resolveCommentAuthor(Content content, Comment comment)
160    {
161        return resolveCommentAuthor(content, comment.getAuthor(), comment.getAuthorEmail());
162    }
163    
164    /**
165     * Try to resolve the user author of the comment as a User from author or email provided in the comment
166     * @param content The content commented
167     * @param commentAuthor The user identity of the comment's author if it exists (null for unauthenticated comment), used to get the user's language
168     * @param commentAuthorEmail The email of the comment's author, used to find a user with this email on the content's site and get its language if the author user identity is not provided
169     * @return The user author or null if not found
170     */
171    protected User resolveCommentAuthor(Content content, UserIdentity commentAuthor, String commentAuthorEmail)
172    {
173        if (commentAuthor != null)
174        {
175            return _userManager.getUser(commentAuthor);
176        }
177        else
178        {
179            Request request = ContextHelper.getRequest(_context);
180            String siteName = WebHelper.getSiteName(request, content);
181            
182            List<String> userPopulationsContexts = new ArrayList<>();
183            userPopulationsContexts.add("/sites/" + siteName);
184            userPopulationsContexts.add("/sites-fo/" + siteName);
185            
186            Set<String> userPopulationsOnSite = _populationContextHelper.getUserPopulationsOnContexts(userPopulationsContexts, false, false);
187            try
188            {
189                return _userManager.getUserByEmail(userPopulationsOnSite, commentAuthorEmail);
190            }
191            catch (NotUniqueUserException e)
192            {
193                getLogger().warn("Cannot find user with email '{}'. Multiple users found with this email", commentAuthorEmail, e);
194                return null;
195            }
196        }
197    }
198    
199    /**
200     * Get the email sender for the mail notification of a comment
201     * @param content The content commented
202     * @return The email sender for the mail notification of a comment
203     */
204    protected String getMailSender(Content content)
205    {
206        Request request = ContextHelper.getRequest(_context);
207        String siteName = WebHelper.getSiteName(request, content);
208        Site site = _siteManager.getSite(siteName);
209        if (site != null)
210        {
211            return site.getValue("site-mail-from");
212        }
213        
214        return Config.getInstance().getValue("smtp.mail.from");
215    }
216    
217    /**
218     * Get the email recipient for the mail notification of a comment
219     * @param comment The comment
220     * @return The email recipient for the mail notification of a comment. Can be null or empty.
221     */
222    protected String getMailRecipient(Comment comment)
223    {
224        String recipient = comment.getAuthorEmail();
225        if (StringUtils.isBlank(recipient))
226        {
227            recipient = Optional.ofNullable(comment.getAuthor())
228                    .map(_userManager::getUser)
229                    .filter(Objects::nonNull)
230                    .map(User::getEmail)
231                    .orElse(null);
232                    
233        }
234        return recipient;
235    }
236    
237}