001/* 002 * Copyright 2022 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.plugins.forms.helper; 017 018import java.io.IOException; 019import java.io.InputStreamReader; 020import java.io.Reader; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026import java.util.Objects; 027import java.util.Optional; 028import java.util.stream.Collectors; 029 030import org.apache.avalon.framework.component.Component; 031import org.apache.avalon.framework.context.Context; 032import org.apache.avalon.framework.context.ContextException; 033import org.apache.avalon.framework.context.Contextualizable; 034import org.apache.avalon.framework.service.ServiceException; 035import org.apache.avalon.framework.service.ServiceManager; 036import org.apache.avalon.framework.service.Serviceable; 037import org.apache.cocoon.components.ContextHelper; 038import org.apache.cocoon.environment.Request; 039import org.apache.commons.io.IOUtils; 040import org.apache.commons.lang3.ArrayUtils; 041import org.apache.commons.lang3.StringUtils; 042import org.apache.excalibur.source.Source; 043import org.apache.excalibur.source.SourceResolver; 044 045import org.ametys.cms.data.File; 046import org.ametys.core.ui.Callable; 047import org.ametys.core.ui.mail.StandardMailBodyHelper; 048import org.ametys.core.user.User; 049import org.ametys.core.user.UserIdentity; 050import org.ametys.core.user.UserManager; 051import org.ametys.core.util.I18nUtils; 052import org.ametys.core.util.mail.SendMailHelper; 053import org.ametys.core.util.mail.SendMailHelper.MailBuilder; 054import org.ametys.core.util.mail.SendMailHelper.NamedStream; 055import org.ametys.plugins.forms.dao.FormDAO; 056import org.ametys.plugins.forms.dao.FormEntryDAO; 057import org.ametys.plugins.forms.dao.FormQuestionDAO; 058import org.ametys.plugins.forms.dao.FormQuestionDAO.FormEntryValues; 059import org.ametys.plugins.forms.question.FormQuestionType; 060import org.ametys.plugins.forms.question.sources.AbstractSourceType; 061import org.ametys.plugins.forms.question.sources.ManualWithEmailSourceType; 062import org.ametys.plugins.forms.question.sources.UsersSourceType; 063import org.ametys.plugins.forms.question.types.impl.ChoicesListQuestionType; 064import org.ametys.plugins.forms.question.types.impl.FileQuestionType; 065import org.ametys.plugins.forms.question.types.impl.SimpleTextQuestionType; 066import org.ametys.plugins.forms.repository.Form; 067import org.ametys.plugins.forms.repository.FormEntry; 068import org.ametys.plugins.forms.repository.FormQuestion; 069import org.ametys.plugins.forms.rights.FormsDirectoryRightAssignmentContext; 070import org.ametys.plugins.repository.AmetysObjectResolver; 071import org.ametys.runtime.config.Config; 072import org.ametys.runtime.i18n.I18nizableText; 073import org.ametys.runtime.plugin.component.AbstractLogEnabled; 074import org.ametys.web.repository.page.Page; 075import org.ametys.web.repository.site.Site; 076 077import jakarta.mail.MessagingException; 078 079/** 080 * The helper for form mail dialog 081 */ 082public class FormMailHelper extends AbstractLogEnabled implements Serviceable, Component, Contextualizable 083{ 084 085 /** Avalon ROLE. */ 086 public static final String ROLE = FormMailHelper.class.getName(); 087 088 /** The param key for read restriction enable */ 089 public static final String READ_RESTRICTION_ENABLE = "read-restriction-enable"; 090 091 /** The value for entry user in the receiver combobox */ 092 public static final String RECEIVER_COMBOBOX_ENTRY_USER_VALUE = "entry-user"; 093 094 /** The empty value in the receiver combobox */ 095 public static final String RECEIVER_COMBOBOX_INPUT_ONLY = "input-only"; 096 097 /** The request key to ignore right */ 098 public static final String IGNORE_RIGHT_KEY = "ignore-right"; 099 100 /** Pattern for adding entry in acknowledgement of receipt if present in body */ 101 protected static final String _FORM_ENTRY_PATTERN = "{form}"; 102 103 /** Ametys object resolver. */ 104 protected AmetysObjectResolver _resolver; 105 106 /** I18n Utils */ 107 protected I18nUtils _i18nUtils; 108 109 /** The user manager */ 110 protected UserManager _userManager; 111 112 /** The source resolver. */ 113 protected SourceResolver _sourceResolver; 114 115 /** The context */ 116 protected Context _context; 117 118 /** The analyse of files for virus helper */ 119 protected FormDAO _formDAO; 120 121 /** The form question DAO */ 122 protected FormQuestionDAO _formQuestionDAO; 123 124 /** The form entry DAO */ 125 protected FormEntryDAO _formEntryDAO; 126 127 /** The form admin mail helper */ 128 protected FormAdminMailsHelper _formAdminMailsHelper; 129 130 /** 131 * The type of limitation for the mail 132 */ 133 public enum LimitationMailType 134 { 135 /** The mail for queue */ 136 QUEUE, 137 /** The mail for the limit */ 138 LIMIT; 139 } 140 141 public void service(ServiceManager manager) throws ServiceException 142 { 143 _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 144 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 145 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 146 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 147 _formDAO = (FormDAO) manager.lookup(FormDAO.ROLE); 148 _formQuestionDAO = (FormQuestionDAO) manager.lookup(FormQuestionDAO.ROLE); 149 _formEntryDAO = (FormEntryDAO) manager.lookup(FormEntryDAO.ROLE); 150 _formAdminMailsHelper = (FormAdminMailsHelper) manager.lookup(FormAdminMailsHelper.ROLE); 151 } 152 153 public void contextualize(Context context) throws ContextException 154 { 155 _context = context; 156 } 157 158 /** 159 * Get the fields with email regex constraint so they can be used as receivers address for form admin mail 160 * @param formId Id of the form 161 * @return a map of the form question, key is the question id,value is the question title 162 */ 163 @Callable (rights = FormDAO.HANDLE_FORMS_RIGHT_ID, rightContext = FormsDirectoryRightAssignmentContext.ID, paramIndex = 0) 164 public Map<String, Object> getAvailableAdminReceiverFields(String formId) 165 { 166 List<Object> receiverfields = new ArrayList<>(); 167 168 for (FormQuestion question : getQuestionWithMail(formId)) 169 { 170 Map<String, String> properties = new HashMap<>(); 171 properties.put("id", question.getNameForForm()); 172 properties.put("title", question.getTitle()); 173 receiverfields.add(properties); 174 } 175 176 return Map.of("data", receiverfields); 177 } 178 179 /** 180 * Get the fields with email regex constraint so they can be used as receivers address for form receipt mail 181 * @param formId Id of the form 182 * @return a map of the form question, key is the question id,value is the question title 183 */ 184 @SuppressWarnings("unchecked") 185 @Callable (rights = FormDAO.HANDLE_FORMS_RIGHT_ID, rightContext = FormsDirectoryRightAssignmentContext.ID, paramIndex = 0) 186 public Map<String, Object> getAvailableReceiverFields(String formId) 187 { 188 Map<String, Object> results = getAvailableAdminReceiverFields(formId); 189 List<Object> receiverfields = (List<Object>) results.get("data"); 190 191 Map<String, String> properties = new HashMap<>(); 192 properties.put("id", RECEIVER_COMBOBOX_ENTRY_USER_VALUE); 193 properties.put("title", _i18nUtils.translate(new I18nizableText("plugin.forms", "PLUGINS_FORMS_ACKNOWLEDGEMENT_RECEIPT_ENTRY_USER"))); 194 receiverfields.add(properties); 195 196 return Map.of("data", receiverfields); 197 } 198 199 /** 200 * Get the list of questions which return a mail 201 * @param formId the form id 202 * @return the list of questions 203 */ 204 public List<FormQuestion> getQuestionWithMail(String formId) 205 { 206 List<FormQuestion> questions = new ArrayList<>(); 207 Form form = _resolver.resolveById(formId); 208 209 for (FormQuestion question : form.getQuestions()) 210 { 211 FormQuestionType type = question.getType(); 212 if (type instanceof SimpleTextQuestionType) 213 { 214 if (question.hasValue(SimpleTextQuestionType.ATTRIBUTE_REGEXP) && question.getValue(SimpleTextQuestionType.ATTRIBUTE_REGEXP).equals(SimpleTextQuestionType.EMAIL_REGEX_VALUE)) 215 { 216 questions.add(question); 217 } 218 } 219 else if (type instanceof ChoicesListQuestionType cLType && (cLType.getSourceType(question) instanceof UsersSourceType || cLType.getSourceType(question) instanceof ManualWithEmailSourceType)) 220 { 221 questions.add(question); 222 } 223 } 224 225 return questions; 226 } 227 228 /** 229 * Get all the email addresses from manual entry and/or a form field 230 * @param form the form 231 * @param entry the last entry 232 * @return an array of all the admin emails 233 */ 234 public String[] getAdminEmails(Form form, FormEntry entry) 235 { 236 Optional<String[]> adminEmails = form.getAdminEmails(); 237 Optional<String> otherAdminEmails = form.getOtherAdminEmails(); 238 String[] emailsAsArray = adminEmails.isPresent() ? adminEmails.get() : ArrayUtils.EMPTY_STRING_ARRAY; 239 Optional<String> otherEmailSource = getReceiver(entry, otherAdminEmails); 240 String[] mergedEmails = emailsAsArray; 241 if (otherEmailSource.isPresent()) 242 { 243 String otherEmails = otherEmailSource.get(); 244 String[] emails = otherEmails.split("[ ,;\r]"); 245 List<String> validEmails = _formAdminMailsHelper.getValidAdminEmails(emails); 246 if (!validEmails.isEmpty()) 247 { 248 mergedEmails = ArrayUtils.addAll(emailsAsArray, validEmails.toArray(new String[validEmails.size()])); 249 } 250 else 251 { 252 getLogger().error("Mails addresses " + otherEmails + " did not match regex"); 253 } 254 } 255 return mergedEmails; 256 } 257 258 /** 259 * Get the receiver from the entry 260 * @param entry the entry 261 * @param receiverPath the path to get receiver 262 * @return the receiver from the entry 263 */ 264 public Optional<String> getReceiver(FormEntry entry, Optional<String> receiverPath) 265 { 266 if (receiverPath.isEmpty()) 267 { 268 return Optional.empty(); 269 } 270 271 if (receiverPath.get().equals(RECEIVER_COMBOBOX_ENTRY_USER_VALUE)) 272 { 273 UserIdentity userIdentity = entry.getUser(); 274 User user = _userManager.getUser(userIdentity); 275 return Optional.ofNullable(user != null ? user.getEmail() : null); 276 } 277 else 278 { 279 FormQuestion question = entry.getForm().getQuestion(receiverPath.get()); 280 FormQuestionType type = question.getType(); 281 if (type instanceof SimpleTextQuestionType) 282 { 283 return Optional.ofNullable(entry.getValue(receiverPath.get())); 284 } 285 else if (type instanceof ChoicesListQuestionType cLType) 286 { 287 if (cLType.getSourceType(question) instanceof UsersSourceType && !entry.isMultiple(receiverPath.get())) 288 { 289 UserIdentity userIdentity = entry.getValue(receiverPath.get()); 290 if (userIdentity != null) 291 { 292 User user = _userManager.getUser(userIdentity); 293 return user != null ? Optional.ofNullable(user.getEmail()) : Optional.empty(); 294 } 295 } 296 else if (cLType.getSourceType(question) instanceof ManualWithEmailSourceType manualWithEmail) 297 { 298 String value = entry.getValue(receiverPath.get()); 299 Map<String, Object> enumParam = new HashMap<>(); 300 enumParam.put(AbstractSourceType.QUESTION_PARAM_KEY, question); 301 try 302 { 303 return Optional.ofNullable(manualWithEmail.getEntryEmail(value, enumParam)); 304 } 305 catch (Exception e) 306 { 307 getLogger().error("Could not get email from question '" + question.getNameForForm() + "' with value '" + value + "'"); 308 } 309 } 310 } 311 312 return Optional.empty(); 313 } 314 } 315 316 /** 317 * Send the notification emails. 318 * @param form the form. 319 * @param entry the user input. 320 * @param adminEmails list of email address where to send the notification 321 */ 322 public void sendEmailsForAdmin(Form form, FormEntry entry, String[] adminEmails) 323 { 324 try 325 { 326 String lang = StringUtils.defaultIfBlank(form.getAdminEmailLanguage().orElse(null), _formDAO.getFormLocale(form)); 327 String sender = Config.getInstance().getValue("smtp.mail.from"); 328 UserIdentity userIdentity = entry.getUser(); 329 User user = userIdentity != null ? _userManager.getUser(userIdentity) : null; 330 331 // Get subject 332 Optional<String> adminEmailSubject = form.getAdminEmailSubject(); 333 String subject = adminEmailSubject.isPresent() 334 ? _replaceVariablesAndBreaks(form, user, adminEmailSubject.get(), lang, false) 335 : null; 336 337 // Get body with details 338 Optional<String> adminEmailBody = form.getAdminEmailBody(); 339 String message = adminEmailBody.isPresent() 340 ? _replaceVariablesAndBreaks(form, user, adminEmailBody.get(), lang, true) 341 : null; 342 343 String entryDetails = getMail("entry.html", entry, Map.of(), false); 344 345 String prettyHtmlBody = StandardMailBodyHelper.newHTMLBody() 346 .withLanguage(lang) 347 .withTitle(org.ametys.core.util.StringUtils.escapeHTML(subject)) 348 .withMessage(message) 349 .withDetails(new I18nizableText("plugin.forms", "PLUGINS_FORMS_MAIL_RESULTS_DETAILS_TITLE"), entryDetails, false) 350 .build(); 351 352 // Get mail as text 353 String params = "?type=results&form-name=" + form.getTitle() + "&locale=" + lang; 354 String text = getMail("results.txt" + params, entry, Map.of(), false); 355 356 // Send mails 357 for (String email : adminEmails) 358 { 359 if (StringUtils.isNotEmpty(email)) 360 { 361 _sendMail(form, entry, subject, prettyHtmlBody, text, sender, email, false, false); 362 } 363 } 364 } 365 catch (IOException e) 366 { 367 getLogger().error("Error creating the notification message.", e); 368 } 369 } 370 371 private String _replaceVariablesAndBreaks(Form form, User user, String mailText, String language, boolean escapeHTML) 372 { 373 String text = mailText; 374 text = StringUtils.replace(text, "{site}", form.getSite().getTitle()); 375 text = StringUtils.replace(text, "{user}", user != null ? user.getFullName() : _i18nUtils.translate(new I18nizableText("plugin.forms", "PLUGINS_FORMS_ADMIN_EMAILS_USER_ANONYMOUS"), language)); 376 text = StringUtils.replace(text, "{title}", form.getTitle()); 377 return escapeHTML 378 ? org.ametys.core.util.StringUtils.escapeHTML(text).replaceAll("\r?\n", "<br/>") 379 : text.replaceAll("\r?\n", "<br/>"); 380 } 381 382 /** 383 * Send limitation mail when the limit is reached 384 * @param entry the form entry 385 * @param adminEmails list of email address where to send the notification 386 * @param limitationType the type of limitation 387 */ 388 public void sendLimitationReachedMailForAdmin(FormEntry entry, String[] adminEmails, LimitationMailType limitationType) 389 { 390 try 391 { 392 Form form = entry.getForm(); 393 394 String lang = StringUtils.defaultIfBlank(form.getAdminEmailLanguage().orElse(null), _formDAO.getFormLocale(form)); 395 String sender = Config.getInstance().getValue("smtp.mail.from"); 396 397 String escapedFormTitle = org.ametys.core.util.StringUtils.escapeHTML(form.getTitle()); 398 String escapedSiteTitle = org.ametys.core.util.StringUtils.escapeHTML(form.getSite().getTitle()); 399 400 // Get subject 401 I18nizableText subject = limitationType == LimitationMailType.LIMIT 402 ? new I18nizableText("plugin.forms", "PLUGINS_FORMS_MAIL_LIMIT_SUBJECT", List.of(escapedFormTitle, escapedSiteTitle)) 403 : new I18nizableText("plugin.forms", "PLUGINS_FORMS_MAIL_QUEUE_SUBJECT", List.of(escapedFormTitle, escapedSiteTitle)); 404 405 // Get body 406 I18nizableText message = limitationType == LimitationMailType.LIMIT 407 ? new I18nizableText("plugin.forms", "PLUGINS_FORMS_MAIL_LIMIT_TEXT", List.of(escapedFormTitle)) 408 : new I18nizableText("plugin.forms", "PLUGINS_FORMS_MAIL_QUEUE_TEXT", List.of(escapedFormTitle)); 409 410 String prettyHtmlBody = StandardMailBodyHelper.newHTMLBody() 411 .withLanguage(lang) 412 .withTitle(subject) 413 .withMessage(message) 414 .build(); 415 416 // Get mail as text 417 I18nizableText textI18n = limitationType == LimitationMailType.LIMIT 418 ? new I18nizableText("plugin.forms", "PLUGINS_FORMS_MAIL_LIMIT_TEXT_NO_HTML", List.of(escapedFormTitle)) 419 : new I18nizableText("plugin.forms", "PLUGINS_FORMS_MAIL_QUEUE_TEXT_NO_HTML", List.of(escapedFormTitle)); 420 String text = _i18nUtils.translate(textI18n, lang); 421 422 for (String email : adminEmails) 423 { 424 if (StringUtils.isNotEmpty(email)) 425 { 426 _sendMail(form, entry, _i18nUtils.translate(subject, lang), prettyHtmlBody, text, sender, email, false, false); 427 } 428 } 429 } 430 catch (IOException e) 431 { 432 getLogger().error("Error creating the limit message.", e); 433 } 434 } 435 436 /** 437 * Send the receipt email. 438 * @param form the form. 439 * @param entry the current entry 440 */ 441 public void sendReceiptEmail(Form form, FormEntry entry) 442 { 443 if (form.getReceiptSender().isPresent()) 444 { 445 Optional<String> receiver = getReceiver(entry, form.getReceiptReceiver()); 446 if (receiver.isPresent()) 447 { 448 String lang = _formDAO.getFormLocale(form); 449 450 String sender = form.getReceiptSender().get(); 451 String subject = form.getReceiptSubject().get(); 452 String bodyTxt = form.getReceiptBody().get(); 453 String bodyHTML = org.ametys.core.util.StringUtils.escapeHTML(bodyTxt).replaceAll("\r?\n", "<br/>"); 454 455 try 456 { 457 String prettyHtmlBody = StandardMailBodyHelper.newHTMLBody() 458 .withLanguage(lang) 459 .withTitle(org.ametys.core.util.StringUtils.escapeHTML(subject)) 460 .withMessage(bodyHTML) 461 .build(); 462 463 // Always check reading restriction for the receipt email 464 _sendMail(form, entry, subject, prettyHtmlBody, bodyTxt, sender, receiver.get(), true, true); 465 } 466 catch (IOException e) 467 { 468 getLogger().error("An error occurred sending receipt mail to '{}'", receiver.get(), e); 469 } 470 } 471 } 472 } 473 474 /** 475 * Send mail when the form entry if out of the queue 476 * @param form the form 477 * @param entry the form entry 478 */ 479 public void sendOutOfQueueMail(Form form, FormEntry entry) 480 { 481 Optional<String> receiver = getReceiver(entry, form.getQueueMailReceiver()); 482 if (receiver.isPresent()) 483 { 484 String lang = _formDAO.getFormLocale(form); 485 486 String sender = form.getQueueMailSender().get(); 487 String subject = form.getQueueMailSubject().get(); 488 String bodyTxt = form.getQueueMailBody().get(); 489 String bodyHTML = org.ametys.core.util.StringUtils.escapeHTML(bodyTxt).replaceAll("\r?\n", "<br/>"); 490 491 try 492 { 493 String prettyHtmlBody = StandardMailBodyHelper.newHTMLBody() 494 .withLanguage(lang) 495 .withTitle(org.ametys.core.util.StringUtils.escapeHTML(subject)) 496 .withMessage(bodyHTML) 497 .build(); 498 499 // Always check reading restriction for the receipt email 500 _sendMail(form, entry, subject, prettyHtmlBody, bodyTxt, sender, receiver.get(), true, true); 501 } 502 catch (IOException e) 503 { 504 getLogger().error("An error occurred sending mail to get out of the queue to '{}'", receiver.get(), e); 505 } 506 } 507 } 508 509 /** 510 * Send invitation mails to the users 511 * @param form the form 512 * @param users the users to receive the mail 513 * @param message the invitation message 514 * @param language The language to use in the mail. Can be null, default to form's language 515 */ 516 public void sendInvitationMails(Form form, List<User> users, String message, String language) 517 { 518 String formLang = _formDAO.getFormLocale(form); 519 String lang = StringUtils.defaultIfBlank(language, formLang); 520 String sender = Config.getInstance().getValue("smtp.mail.from"); 521 522 // Get subject 523 I18nizableText subject = new I18nizableText("plugin.forms", "PLUGINS_FORMS_SEND_INVITATIONS_BOX_SUBJECT"); 524 525 // Get body 526 Site site = form.getSite(); 527 String formURI = _getFormURI(form, formLang); 528 529 String replacedMessage = StringUtils.replace(org.ametys.core.util.StringUtils.escapeHTML(message), "{site}", org.ametys.core.util.StringUtils.escapeHTML(site.getTitle())); 530 String textMessage = StringUtils.replace(replacedMessage, "{link}", formURI); 531 String htmlMessage = StringUtils.replace(replacedMessage, "{link}", "<a href='" + formURI + "'>" + formURI + "</a>"); 532 533 for (User user : users) 534 { 535 try 536 { 537 String finalTextMessage = StringUtils.replace(textMessage, "{name}", user.getFullName()); 538 String finalHtmlMessage = StringUtils.replace(htmlMessage, "{name}", user.getFullName()); 539 String prettyHtmlBody = StandardMailBodyHelper.newHTMLBody() 540 .withLanguage(lang) 541 .withTitle(subject) 542 .withMessage(finalHtmlMessage) 543 .withLink(formURI, new I18nizableText("plugin.forms", "PLUGINS_FORMS_SEND_INVITATIONS_LINK_LABEL")) 544 .build(); 545 546 _sendMail(form, null, _i18nUtils.translate(subject, lang), prettyHtmlBody, finalTextMessage, sender, user.getEmail(), false, true); 547 } 548 catch (IOException e) 549 { 550 getLogger().error("Unable to send invitation mail to user {}", user.getEmail(), e); 551 } 552 } 553 } 554 555 /** 556 * Get the form page URI 557 * @param form the form 558 * @param language the language 559 * @return the form page URI 560 */ 561 protected String _getFormURI (Form form, String language) 562 { 563 Site site = form.getSite(); 564 Optional<Page> page = _formDAO.getFormPage(form.getId(), site.getName()) 565 .stream() 566 .filter(Page.class::isInstance) 567 .map(Page.class::cast) 568 .filter(p -> p.getSitemapName().equals(language)) 569 .findAny(); 570 571 return page.map(p -> site.getUrl() + "/" + p.getSitemap().getName() + "/" + p.getPathInSitemap() + ".html") 572 .orElse(null); 573 } 574 575 /** 576 * Get a mail pipeline's content. 577 * @param resource the mail resource pipeline 578 * @param entry the user input. 579 * @param additionalParameters the additional parameters 580 * @param withReadingRestriction <code>true</code> to enable reading restriction for form data in the mail 581 * @return the mail content. 582 * @throws IOException if an error occurs. 583 */ 584 public String getMail(String resource, FormEntry entry, Map<String, Object> additionalParameters, boolean withReadingRestriction) throws IOException 585 { 586 Source src = null; 587 Request request = ContextHelper.getRequest(_context); 588 Form form = entry.getForm(); 589 590 try 591 { 592 request.setAttribute(IGNORE_RIGHT_KEY, true); 593 594 String uri = "cocoon:/mail/entry/" + resource; 595 Map<String, Object> parameters = new HashMap<>(); 596 parameters.put("formId", form.getId()); 597 parameters.put("entryId", entry.getId()); 598 parameters.put(READ_RESTRICTION_ENABLE, withReadingRestriction); 599 600 parameters.putAll(additionalParameters); 601 602 src = _sourceResolver.resolveURI(uri, null, parameters); 603 Reader reader = new InputStreamReader(src.getInputStream(), "UTF-8"); 604 return IOUtils.toString(reader); 605 } 606 finally 607 { 608 request.setAttribute(IGNORE_RIGHT_KEY, false); 609 _sourceResolver.release(src); 610 } 611 } 612 613 /** 614 * Get the files of a user input. 615 * @param form The current form 616 * @param entry The current entry 617 * @param onlyWritableQuestion <code>true</code> to have only writable question 618 * @param onlyReadableQuestion <code>true</code> to have only readable question 619 * @return the files submitted by the user. 620 */ 621 public Collection<NamedStream> getEntryFiles(Form form, FormEntry entry, boolean onlyWritableQuestion, boolean onlyReadableQuestion) 622 { 623 Optional<Long> currentStepId = entry.getForm().hasWorkflow() ? Optional.of(_formEntryDAO.getCurrentStepId(entry)) : Optional.empty(); 624 return _formQuestionDAO.getRuleFilteredQuestions(form, new FormEntryValues(null, entry), currentStepId, onlyWritableQuestion, onlyReadableQuestion).stream() 625 .filter(q -> q.getType() instanceof FileQuestionType) 626 .map(q -> entry.<File>getValue(q.getNameForForm())) 627 .filter(Objects::nonNull) 628 .map(f -> new NamedStream(f.getInputStream(), f.getName(), f.getMimeType())) 629 .collect(Collectors.toList()); 630 } 631 632 private void _sendMail(Form form, FormEntry entry, String subject, String html, String text, String sender, String recipient, boolean addFormInformation, boolean withReadingRestriction) 633 { 634 try 635 { 636 String htmlBody = html; 637 String textBody = text; 638 639 Collection<NamedStream> records = entry != null ? getEntryFiles(form, entry, false, false) : new ArrayList<>(); 640 try 641 { 642 if (addFormInformation) 643 { 644 if (textBody.contains(_FORM_ENTRY_PATTERN)) 645 { 646 String entry2text = getMail("entry.txt", entry, Map.of(), withReadingRestriction); 647 textBody = StringUtils.replace(textBody, _FORM_ENTRY_PATTERN, entry2text); 648 } 649 650 if (htmlBody.contains(_FORM_ENTRY_PATTERN)) 651 { 652 String entry2html = getMail("entry.html", entry, Map.of(), withReadingRestriction); 653 htmlBody = StringUtils.replace(htmlBody, _FORM_ENTRY_PATTERN, entry2html); 654 } 655 } 656 657 MailBuilder mailBuilder = SendMailHelper.newMail() 658 .withAsync(true) 659 .withSubject(subject) 660 .withHTMLBody(htmlBody) 661 .withTextBody(textBody) 662 .withSender(sender) 663 .withRecipient(recipient); 664 665 if (!records.isEmpty()) 666 { 667 mailBuilder = mailBuilder.withAttachmentsAsStream(records); 668 } 669 mailBuilder.sendMail(); 670 } 671 finally 672 { 673 for (NamedStream record : records) 674 { 675 IOUtils.close(record.inputStream()); 676 } 677 } 678 } 679 catch (MessagingException | IOException e) 680 { 681 getLogger().error("Error sending the mail to " + recipient, e); 682 } 683 } 684}