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.io.IOException; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022 023import org.apache.avalon.framework.service.ServiceException; 024import org.apache.avalon.framework.service.ServiceManager; 025import org.apache.cocoon.components.ContextHelper; 026import org.apache.cocoon.environment.Request; 027import org.apache.commons.lang3.StringUtils; 028 029import org.ametys.cms.ObservationConstants; 030import org.ametys.cms.repository.Content; 031import org.ametys.cms.repository.comment.Comment; 032import org.ametys.cms.transformation.xslt.ResolveURIComponent; 033import org.ametys.core.observation.Event; 034import org.ametys.core.ui.mail.StandardMailBodyHelper; 035import org.ametys.core.ui.mail.StandardMailBodyHelper.MailBodyBuilder; 036import org.ametys.core.ui.mail.StandardMailBodyHelper.MailBodyBuilder.UnauthenticatedUserInput; 037import org.ametys.core.ui.mail.StandardMailBodyHelper.MailBodyBuilder.UserInput; 038import org.ametys.core.user.User; 039import org.ametys.core.util.mail.SendMailHelper; 040import org.ametys.runtime.i18n.I18nizableText; 041import org.ametys.runtime.i18n.I18nizableTextParameter; 042import org.ametys.web.WebHelper; 043import org.ametys.web.renderingcontext.RenderingContext; 044import org.ametys.web.renderingcontext.RenderingContextHandler; 045import org.ametys.web.repository.content.WebContent; 046import org.ametys.web.repository.page.Page; 047import org.ametys.web.repository.site.Site; 048 049import jakarta.mail.MessagingException; 050 051/** 052 * Observer to notify the comment's author when someone reply to its commments 053 */ 054public class NotifyCommentAuthorWhenReplyingObserver extends AbstractNotifyCommentAuthorObserver 055{ 056 /** The rendering context handler */ 057 protected RenderingContextHandler _renderingContextHandler; 058 059 @Override 060 public void service(ServiceManager smanager) throws ServiceException 061 { 062 super.service(smanager); 063 _renderingContextHandler = (RenderingContextHandler) smanager.lookup(RenderingContextHandler.ROLE); 064 } 065 066 public boolean supports(Event event) 067 { 068 if (event.getId().equals(ObservationConstants.EVENT_CONTENT_COMMENT_VALIDATED)) 069 { 070 Comment comment = (Comment) event.getArguments().get(ObservationConstants.ARGS_COMMENT); 071 return comment != null && comment.getCommentParent() != null; 072 } 073 return false; 074 } 075 076 public void observe(Event event, Map<String, Object> transientVars) throws Exception 077 { 078 Map<String, Object> arguments = event.getArguments(); 079 Content content = (Content) arguments.get(ObservationConstants.ARGS_CONTENT); 080 Comment comment = (Comment) arguments.get(ObservationConstants.ARGS_COMMENT); 081 082 Comment parentComment = comment.getCommentParent(); 083 String recipient = getMailRecipient(parentComment); 084 if (StringUtils.isEmpty(recipient)) 085 { 086 getLogger().info("The parent comment {} of content {} has no author email, no notification mail will be sent", parentComment.getId(), content.getId()); 087 return; 088 } 089 090 // Try to get authenticated users related to parent comment and reply 091 User parentCommentUser = resolveCommentAuthor(content, parentComment); 092 User replyCommentUser = resolveCommentAuthor(content, comment); 093 094 // Do not send mail if the author of the comment and the author of the reply are the same 095 if (parentCommentUser != null && replyCommentUser != null && parentCommentUser == replyCommentUser) 096 { 097 return; 098 } 099 else if (recipient.equalsIgnoreCase(comment.getAuthorEmail())) 100 { 101 return; 102 } 103 104 String sender = getMailSender(content); 105 String lang = getMailLanguage(content, parentCommentUser); 106 107 try 108 { 109 Page page = null; 110 if (content instanceof WebContent webContent) 111 { 112 page = webContent.getReferencingPages().stream().findFirst().orElse(null); 113 } 114 115 I18nizableText i18nSubject = getMailSubject(content, page); 116 String subject = _i18nUtils.translate(i18nSubject, lang); 117 I18nizableText i18nTitle = getMailTitle(content, page); 118 I18nizableText i18nBody = getMailBody(content, page, parentComment, comment); 119 120 MailBodyBuilder bodyBuilder = StandardMailBodyHelper.newHTMLBody() 121 .withLanguage(lang) 122 .withTitle(i18nTitle) 123 .withMessage(i18nBody) 124 .withFooterBottomText(StandardMailBodyHelper.AUTOGENERATED_DO_NOT_ANSWER_TEXT); 125 126 if (replyCommentUser != null) 127 { 128 bodyBuilder.withUserInputs(List.of(new UserInput(replyCommentUser, comment.getCreationDate(), comment.getContent())), new I18nizableText("plugin.web", "PLUGINS_WEB_CONTENT_COMMENTS_NEW_REPLY_REPLY_HEADER")); 129 } 130 else 131 { 132 bodyBuilder.withUnauthenticatedUserInputs(List.of(new UnauthenticatedUserInput(comment.getAuthorName(), comment.getCreationDate(), comment.getContent())), new I18nizableText("plugin.web", "PLUGINS_WEB_CONTENT_COMMENTS_NEW_REPLY_REPLY_HEADER")); 133 } 134 135 if (parentCommentUser != null) 136 { 137 bodyBuilder.addUserInputs(List.of(new UserInput(parentCommentUser, parentComment.getCreationDate(), parentComment.getContent())), new I18nizableText("plugin.web", "PLUGINS_WEB_CONTENT_COMMENTS_NEW_REPLY_COMMENT_HEADER")); 138 } 139 else 140 { 141 bodyBuilder.addUnauthenticatedUserInput(List.of(new UnauthenticatedUserInput(parentComment.getAuthorName(), parentComment.getCreationDate(), parentComment.getContent())), new I18nizableText("plugin.web", "PLUGINS_WEB_CONTENT_COMMENTS_NEW_REPLY_COMMENT_HEADER")); 142 } 143 144 String linkUrl = page != null ? getPageUri(page) : getOrphanContentUri(content); 145 if (StringUtils.isNotEmpty(linkUrl)) 146 { 147 bodyBuilder.withLink(linkUrl, new I18nizableText("plugin.web", "PLUGINS_WEB_CONTENT_COMMENTS_NEW_REPLY_LINK_TITLE")); 148 } 149 150 String htmlBody = bodyBuilder.build(); 151 152 SendMailHelper.newMail() 153 .withSubject(subject) 154 .withHTMLBody(htmlBody) 155 .withSender(sender) 156 .withRecipient(recipient) 157 .withAsync(true) 158 .sendMail(); 159 } 160 catch (MessagingException | IOException e) 161 { 162 getLogger().warn("Could not send a notification mail to {}", recipient, e); 163 } 164 165 } 166 167 /** 168 * Get the mail subject 169 * @param content The commented content 170 * @param page The page holding the commented content. Can be null. 171 * @return The mail subject 172 */ 173 protected I18nizableText getMailSubject(Content content, Page page) 174 { 175 return new I18nizableText("plugin.web", "PLUGINS_WEB_CONTENT_COMMENTS_NEW_REPLY_SUBJECT", getSubjectI18nParams(content, page)); 176 } 177 178 /** 179 * Get the mail title 180 * @param content The commented content 181 * @param page The page holding the commented content. Can be null. 182 * @return The mail title 183 */ 184 protected I18nizableText getMailTitle(Content content, Page page) 185 { 186 return new I18nizableText("plugin.web", "PLUGINS_WEB_CONTENT_COMMENTS_NEW_REPLY_TITLE", getSubjectI18nParams(content, page)); 187 } 188 189 /** 190 * Get the mail body 191 * @param content The commented content 192 * @param page The page holding the commented content. Can be null. 193 * @param parentComment The initial comment 194 * @param reply The reply to the comment 195 * @return the mail body 196 */ 197 protected I18nizableText getMailBody(Content content, Page page, Comment parentComment, Comment reply) 198 { 199 if (page != null) 200 { 201 return new I18nizableText("plugin.web", "PLUGINS_WEB_CONTENT_COMMENTS_NEW_REPLY_BODY", getBodyI18nParams(content, parentComment, reply, page.getSite(), page)); 202 } 203 else 204 { 205 Request request = ContextHelper.getRequest(_context); 206 String siteName = WebHelper.getSiteName(request, content); 207 Site site = _siteManager.getSite(siteName); 208 209 return new I18nizableText("plugin.web", "PLUGINS_WEB_CONTENT_COMMENTS_NEW_REPLY_NO_PAGE_BODY", getBodyI18nParams(content, parentComment, reply, site, null)); 210 } 211 } 212 213 /** 214 * Get the i18n parameters for mail subject 215 * @param content the commented content 216 * @param page The page holding the commented content. Can be null. 217 * @return the i18n parameters for mail subject 218 */ 219 protected Map<String, I18nizableTextParameter> getSubjectI18nParams(Content content, Page page) 220 { 221 Map<String, I18nizableTextParameter> i18nparam = new HashMap<>(); 222 223 i18nparam.put("contentTitle", new I18nizableText(_contentHelper.getTitle(content))); 224 225 if (page != null) 226 { 227 i18nparam.put("pageTitle", new I18nizableText(page.getTitle())); 228 } 229 230 if (content instanceof WebContent webContent) 231 { 232 Site site = webContent.getSite(); 233 i18nparam.put("siteTitle", new I18nizableText(site.getTitle())); 234 } 235 236 return i18nparam; 237 } 238 239 /** 240 * Get the i18n parameters for mail body 241 * @param content the commented content 242 * @param parentComment The initial comment 243 * @param subComment The answser to the comment 244 * @param site The site 245 * @param page The page holding the commented content. Can be null. 246 * @return the i18n parameters for mail body 247 */ 248 protected Map<String, I18nizableTextParameter> getBodyI18nParams(Content content, Comment parentComment, Comment subComment, Site site, Page page) 249 { 250 Map<String, I18nizableTextParameter> i18nparam = new HashMap<>(); 251 252 i18nparam.put("userName", new I18nizableText(parentComment.getAuthorName())); 253 i18nparam.put("authorName", new I18nizableText(subComment.getAuthorName())); 254 i18nparam.put("contentTitle", new I18nizableText(_contentHelper.getTitle(content))); 255 256 if (page != null) 257 { 258 i18nparam.put("pageTitle", new I18nizableText(page.getTitle())); 259 i18nparam.put("pageUri", new I18nizableText(getPageUri(page))); 260 } 261 else 262 { 263 i18nparam.put("contentUri", new I18nizableText(getOrphanContentUri(content))); 264 } 265 266 if (site != null) 267 { 268 i18nparam.put("siteTitle", new I18nizableText(site.getTitle())); 269 i18nparam.put("siteUrl", new I18nizableText(site.getUrl())); 270 } 271 272 return i18nparam; 273 } 274 275 /** 276 * Get the absolute front url of an orphan content 277 * @param content the commented content 278 * @return the url of content 279 */ 280 protected String getOrphanContentUri(Content content) 281 { 282 return StringUtils.EMPTY; 283 } 284 285 /** 286 * Get the absolute front url of the page 287 * @param page the page 288 * @return the page url 289 */ 290 protected String getPageUri(Page page) 291 { 292 RenderingContext currentContext = _renderingContextHandler.getRenderingContext(); 293 try 294 { 295 _renderingContextHandler.setRenderingContext(RenderingContext.FRONT); 296 return ResolveURIComponent.resolve("page", page.getId(), false, true, false); 297 } 298 finally 299 { 300 _renderingContextHandler.setRenderingContext(currentContext); 301 } 302 } 303 304}