001/* 002 * Copyright 2023 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.mail; 017 018import java.io.IOException; 019import java.io.InputStreamReader; 020import java.io.Reader; 021import java.time.ZonedDateTime; 022import java.util.ArrayList; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.avalon.framework.activity.Initializable; 028import org.apache.avalon.framework.context.Context; 029import org.apache.avalon.framework.context.Contextualizable; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.avalon.framework.service.Serviceable; 033import org.apache.cocoon.components.ContextHelper; 034import org.apache.cocoon.environment.Request; 035import org.apache.commons.io.IOUtils; 036import org.apache.commons.lang.StringUtils; 037import org.apache.excalibur.source.Source; 038import org.apache.excalibur.source.SourceResolver; 039 040import org.ametys.core.cocoon.ActionResultGenerator; 041import org.ametys.core.user.User; 042import org.ametys.plugins.core.user.UserHelper; 043import org.ametys.runtime.i18n.I18nizableText; 044import org.ametys.web.WebHelper; 045 046/** 047 * Component to build wrapped HTML mail body for report activities 048 */ 049public class ReportActivitiesMailBodyHelper implements Contextualizable, Serviceable, Initializable 050{ 051 /** The avalon context */ 052 protected static Context _context; 053 054 private static SourceResolver _srcResolver; 055 private static UserHelper _userHelper; 056 057 private static ReportActivitiesMailBodyHelper __instance; 058 059 @Override 060 public void contextualize(Context context) 061 { 062 _context = context; 063 } 064 065 public void initialize() throws Exception 066 { 067 __instance = this; 068 } 069 070 @Override 071 public void service(ServiceManager manager) throws ServiceException 072 { 073 _srcResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 074 _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE); 075 } 076 077 /** 078 * Get the URI to resolve wrapped HTML email body 079 * @return the uri 080 */ 081 public String getMailBodyUri() 082 { 083 Request request = ContextHelper.getRequest(_context); 084 085 String siteName = WebHelper.getSiteName(request); 086 087 return "cocoon://_plugins/web/" + siteName + "/wrapped-mail/activities/body.html"; 088 } 089 090 /** 091 * Creates a new mail body builder. 092 * @return the newly created {@link MailBodyBuilder}. 093 */ 094 public static MailBodyBuilder newHTMLBody() 095 { 096 return new MailBodyBuilder(__instance.getMailBodyUri()); 097 } 098 099 /** 100 * Implements the builder pattern for creating and sending emails. 101 */ 102 public static class MailBodyBuilder 103 { 104 private Object _title; 105 private List<Object> _messages; 106 private List<Activity> _activities; 107 private I18nizableText _linkText; 108 private String _linkUrl; 109 private I18nizableText _footerLinkText; 110 private String _footerLinkUrl; 111 private String _footerImgPluginName; 112 private String _footerImgPath; 113 private String _lang; 114 private String _uri; 115 116 MailBodyBuilder(String uri) 117 { 118 _uri = uri; 119 } 120 121 /** 122 * Set the language 123 * @param lang the language 124 * @return this builder 125 */ 126 public MailBodyBuilder withLanguage(String lang) 127 { 128 _lang = lang; 129 return this; 130 } 131 /** 132 * Set the title 133 * @param title the title as String or {@link I18nizableText} 134 * @return this builder 135 */ 136 public MailBodyBuilder withTitle(Object title) 137 { 138 _title = title; 139 return this; 140 } 141 142 /** 143 * Set the main message 144 * @param message the message as String or {@link I18nizableText} 145 * @return this builder 146 */ 147 public MailBodyBuilder withMessage(Object message) 148 { 149 _messages = List.of(message); 150 return this; 151 } 152 153 /** 154 * Add a new message 155 * @param message the message as String or {@link I18nizableText} 156 * @return this builder 157 */ 158 public MailBodyBuilder addMessage(Object message) 159 { 160 if (_messages == null) 161 { 162 _messages = new ArrayList<>(); 163 } 164 165 _messages.add(message); 166 return this; 167 } 168 169 /** 170 * Set the activities 171 * @param activities the activities 172 * @return this builder 173 */ 174 public MailBodyBuilder withActivities(List<Activity> activities) 175 { 176 _activities = activities; 177 return this; 178 } 179 180 /** 181 * Add an activity 182 * @param activity the activity 183 * @return this builder 184 */ 185 public MailBodyBuilder addActivity(Activity activity) 186 { 187 if (_activities == null) 188 { 189 _activities = new ArrayList<>(); 190 } 191 192 _activities.add(activity); 193 return this; 194 } 195 196 /** 197 * Add a main link to be displayed as a button 198 * @param linkUrl the link url 199 * @param linkText the link text 200 * @return this builder 201 */ 202 public MailBodyBuilder withLink(String linkUrl, I18nizableText linkText) 203 { 204 _linkText = linkText; 205 _linkUrl = linkUrl; 206 return this; 207 } 208 209 /** 210 * Add a link to be displayed on footer 211 * @param linkUrl the link url 212 * @param linkText the link text 213 * @return this builder 214 */ 215 public MailBodyBuilder withFooterLink(String linkUrl, I18nizableText linkText) 216 { 217 return withFooterLink(linkUrl, linkText, null, null); 218 } 219 220 /** 221 * Add a link to be displayed on footer 222 * @param linkUrl the link url 223 * @param linkText the link text 224 * @param imgPluginName the plugin name that constains the image 225 * @param imgPath the path of the image in the plugin 226 * @return this builder 227 */ 228 public MailBodyBuilder withFooterLink(String linkUrl, I18nizableText linkText, String imgPluginName, String imgPath) 229 { 230 _footerLinkText = linkText; 231 _footerLinkUrl = linkUrl; 232 _footerImgPluginName = imgPluginName; 233 _footerImgPath = imgPath; 234 return this; 235 } 236 237 /** 238 * Build the body of the email as HTML 239 * @return the HTML body 240 * @throws IOException if an error occurred when building the email 241 */ 242 public String build() throws IOException 243 { 244 Source src = null; 245 Request request = ContextHelper.getRequest(_context); 246 247 try 248 { 249 request.setAttribute(ActionResultGenerator.MAP_REQUEST_ATTR, toJson()); 250 251 Map<String, Object> parameters = new HashMap<>(); 252 parameters.put("lang", _lang); 253 254 src = _srcResolver.resolveURI(_uri, null, parameters); 255 Reader reader = new InputStreamReader(src.getInputStream(), "UTF-8"); 256 return IOUtils.toString(reader); 257 } 258 finally 259 { 260 _srcResolver.release(src); 261 } 262 } 263 264 /** 265 * Get the JSON representation of the email ingredients 266 * @return the JSON ingredients 267 */ 268 public Map<String, Object> toJson() 269 { 270 Map<String, Object> json = new HashMap<>(); 271 272 json.put("title", _title); 273 274 if (_messages != null && !_messages.isEmpty()) 275 { 276 json.put("message", _messages); 277 } 278 279 if (StringUtils.isNotEmpty(_linkUrl)) 280 { 281 json.put("link", Map.of("url", _linkUrl, "text", _linkText)); 282 } 283 284 if (_activities != null && !_activities.isEmpty()) 285 { 286 json.put("activity", _activities.stream() 287 .map(this::_activity2json) 288 .toList()); 289 } 290 291 json.put("footer", _footer2json()); 292 293 return json; 294 } 295 296 private Map<String, Object> _footer2json() 297 { 298 Map<String, Object> footer2json = new HashMap<>(); 299 300 if (_footerLinkUrl != null && _footerLinkText != null) 301 { 302 Map<String, Object> link2json = new HashMap<>(); 303 304 link2json.put("text", _footerLinkText); 305 link2json.put("url", _footerLinkUrl); 306 if (_footerImgPath != null && _footerImgPluginName != null) 307 { 308 link2json.put("imgPluginName", _footerImgPluginName); 309 link2json.put("imgPath", _footerImgPath); 310 } 311 footer2json.put("link", link2json); 312 } 313 314 return footer2json; 315 } 316 317 private Map<String, Object> _activity2json(Activity activity) 318 { 319 Map<String, Object> activity2json = new HashMap<>(); 320 activity2json.put("author", _userHelper.user2json(activity.author())); 321 activity2json.put("date", activity.date()); 322 activity2json.put("description", activity.description()); 323 return activity2json; 324 } 325 326 /** 327 * Record for an activity 328 * @param author The author. 329 * @param date the date of the action 330 * @param description the description if the activity as String or {@link I18nizableText} 331 */ 332 public record Activity(User author, ZonedDateTime date, Object description) { /* empty */ } 333 } 334 335}