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}