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.lang.StringUtils; 041import org.apache.commons.lang3.ArrayUtils; 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 = _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 I18nizableText subject = adminEmailSubject.isPresent() 334 ? _replaceVariablesAndBreaks(form, user, adminEmailSubject.get(), lang) 335 : null; 336 337 // Get body with details 338 Optional<String> adminEmailBody = form.getAdminEmailBody(); 339 I18nizableText message = adminEmailBody.isPresent() 340 ? _replaceVariablesAndBreaks(form, user, adminEmailBody.get(), lang) 341 : null; 342 343 String entryDetails = getMail("entry.html", entry, Map.of(), false); 344 345 String prettyHtmlBody = StandardMailBodyHelper.newHTMLBody() 346 .withLanguage(lang) 347 .withTitle(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, _i18nUtils.translate(subject, lang), 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 I18nizableText _replaceVariablesAndBreaks(Form form, User user, String mailText, String language) 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 text = text.replaceAll("\r?\n", "<br/>"); 378 return new I18nizableText(text); 379 } 380 381 /** 382 * Send limitation mail when the limit is reached 383 * @param entry the form entry 384 * @param adminEmails list of email address where to send the notification 385 * @param limitationType the type of limitation 386 */ 387 public void sendLimitationReachedMailForAdmin(FormEntry entry, String[] adminEmails, LimitationMailType limitationType) 388 { 389 try 390 { 391 Form form = entry.getForm(); 392 393 String lang = _formDAO.getFormLocale(form); 394 String sender = Config.getInstance().getValue("smtp.mail.from"); 395 396 // Get subject 397 I18nizableText subject = limitationType == LimitationMailType.LIMIT 398 ? new I18nizableText("plugin.forms", "PLUGINS_FORMS_MAIL_LIMIT_SUBJECT", List.of(form.getTitle(), form.getSite().getTitle())) 399 : new I18nizableText("plugin.forms", "PLUGINS_FORMS_MAIL_QUEUE_SUBJECT", List.of(form.getTitle(), form.getSite().getTitle())); 400 401 // Get body 402 I18nizableText message = limitationType == LimitationMailType.LIMIT 403 ? new I18nizableText("plugin.forms", "PLUGINS_FORMS_MAIL_LIMIT_TEXT", List.of(form.getTitle())) 404 : new I18nizableText("plugin.forms", "PLUGINS_FORMS_MAIL_QUEUE_TEXT", List.of(form.getTitle())); 405 406 String prettyHtmlBody = StandardMailBodyHelper.newHTMLBody() 407 .withLanguage(lang) 408 .withTitle(subject) 409 .withMessage(message) 410 .build(); 411 412 // Get mail as text 413 I18nizableText textI18n = limitationType == LimitationMailType.LIMIT 414 ? new I18nizableText("plugin.forms", "PLUGINS_FORMS_MAIL_LIMIT_TEXT_NO_HTML", List.of(form.getTitle())) 415 : new I18nizableText("plugin.forms", "PLUGINS_FORMS_MAIL_QUEUE_TEXT_NO_HTML", List.of(form.getTitle())); 416 String text = _i18nUtils.translate(textI18n, lang); 417 418 for (String email : adminEmails) 419 { 420 if (StringUtils.isNotEmpty(email)) 421 { 422 _sendMail(form, entry, _i18nUtils.translate(subject, lang), prettyHtmlBody, text, sender, email, false, false); 423 } 424 } 425 } 426 catch (IOException e) 427 { 428 getLogger().error("Error creating the limit message.", e); 429 } 430 } 431 432 /** 433 * Send the receipt email. 434 * @param form the form. 435 * @param entry the current entry 436 */ 437 public void sendReceiptEmail(Form form, FormEntry entry) 438 { 439 if (form.getReceiptSender().isPresent()) 440 { 441 Optional<String> receiver = getReceiver(entry, form.getReceiptReceiver()); 442 if (receiver.isPresent()) 443 { 444 String lang = _formDAO.getFormLocale(form); 445 446 String sender = form.getReceiptSender().get(); 447 String subject = form.getReceiptSubject().get(); 448 String bodyTxt = form.getReceiptBody().get(); 449 String bodyHTML = bodyTxt.replaceAll("\r?\n", "<br/>"); 450 451 try 452 { 453 String prettyHtmlBody = StandardMailBodyHelper.newHTMLBody() 454 .withLanguage(lang) 455 .withTitle(subject) 456 .withMessage(bodyHTML) 457 .build(); 458 459 // Always check reading restriction for the receipt email 460 _sendMail(form, entry, subject, prettyHtmlBody, bodyTxt, sender, receiver.get(), true, true); 461 } 462 catch (IOException e) 463 { 464 getLogger().error("An error occurred sending receipt mail to '{}'", receiver.get(), e); 465 } 466 } 467 } 468 } 469 470 /** 471 * Send mail when the form entry if out of the queue 472 * @param form the form 473 * @param entry the form entry 474 */ 475 public void sendOutOfQueueMail(Form form, FormEntry entry) 476 { 477 Optional<String> receiver = getReceiver(entry, form.getQueueMailReceiver()); 478 if (receiver.isPresent()) 479 { 480 String lang = _formDAO.getFormLocale(form); 481 482 String sender = form.getQueueMailSender().get(); 483 String subject = form.getQueueMailSubject().get(); 484 String bodyTxt = form.getQueueMailBody().get(); 485 String bodyHTML = bodyTxt.replaceAll("\r?\n", "<br/>"); 486 487 try 488 { 489 String prettyHtmlBody = StandardMailBodyHelper.newHTMLBody() 490 .withLanguage(lang) 491 .withTitle(subject) 492 .withMessage(bodyHTML) 493 .build(); 494 495 // Always check reading restriction for the receipt email 496 _sendMail(form, entry, subject, prettyHtmlBody, bodyTxt, sender, receiver.get(), true, true); 497 } 498 catch (IOException e) 499 { 500 getLogger().error("An error occurred sending mail to get out of the queue to '{}'", receiver.get(), e); 501 } 502 } 503 } 504 505 /** 506 * Send invitation mails to the users 507 * @param form the form 508 * @param users the users to receive the mail 509 * @param message the invitation message 510 */ 511 public void sendInvitationMails(Form form, List<User> users, String message) 512 { 513 String lang = _formDAO.getFormLocale(form); 514 String sender = Config.getInstance().getValue("smtp.mail.from"); 515 516 // Get subject 517 I18nizableText subject = new I18nizableText("plugin.forms", "PLUGINS_FORMS_SEND_INVITATIONS_BOX_SUBJECT"); 518 519 // Get body 520 Site site = form.getSite(); 521 String formURI = _getFormURI(form, lang); 522 523 String replacedMessage = StringUtils.replace(message, "{site}", site.getTitle()); 524 String textMessage = StringUtils.replace(replacedMessage, "{link}", formURI); 525 String htmlMessage = StringUtils.replace(replacedMessage, "{link}", "<a href='" + formURI + "'>" + formURI + "</a>"); 526 527 for (User user : users) 528 { 529 try 530 { 531 String finalTextMessage = StringUtils.replace(textMessage, "{name}", user.getFullName()); 532 String finalHtmlMessage = StringUtils.replace(htmlMessage, "{name}", user.getFullName()); 533 String prettyHtmlBody = StandardMailBodyHelper.newHTMLBody() 534 .withLanguage(lang) 535 .withTitle(subject) 536 .withMessage(finalHtmlMessage) 537 .withLink(formURI, new I18nizableText("plugin.forms", "PLUGINS_FORMS_SEND_INVITATIONS_LINK_LABEL")) 538 .build(); 539 540 _sendMail(form, null, _i18nUtils.translate(subject, lang), prettyHtmlBody, finalTextMessage, sender, user.getEmail(), false, true); 541 } 542 catch (IOException e) 543 { 544 getLogger().error("Unable to send invitation mail to user {}", user.getEmail(), e); 545 } 546 } 547 } 548 549 /** 550 * Get the form page URI 551 * @param form the form 552 * @param language the language 553 * @return the form page URI 554 */ 555 protected String _getFormURI (Form form, String language) 556 { 557 Site site = form.getSite(); 558 Optional<Page> page = _formDAO.getFormPage(form.getId(), site.getName()) 559 .stream() 560 .filter(Page.class::isInstance) 561 .map(Page.class::cast) 562 .filter(p -> p.getSitemapName().equals(language)) 563 .findAny(); 564 565 return page.map(p -> site.getUrl() + "/" + p.getSitemap().getName() + "/" + p.getPathInSitemap() + ".html") 566 .orElse(null); 567 } 568 569 /** 570 * Get a mail pipeline's content. 571 * @param resource the mail resource pipeline 572 * @param entry the user input. 573 * @param additionalParameters the additional parameters 574 * @param withReadingRestriction <code>true</code> to enable reading restriction for form data in the mail 575 * @return the mail content. 576 * @throws IOException if an error occurs. 577 */ 578 public String getMail(String resource, FormEntry entry, Map<String, Object> additionalParameters, boolean withReadingRestriction) throws IOException 579 { 580 Source src = null; 581 Request request = ContextHelper.getRequest(_context); 582 Form form = entry.getForm(); 583 584 try 585 { 586 request.setAttribute(IGNORE_RIGHT_KEY, true); 587 588 String uri = "cocoon:/mail/entry/" + resource; 589 Map<String, Object> parameters = new HashMap<>(); 590 parameters.put("formId", form.getId()); 591 parameters.put("entryId", entry.getId()); 592 parameters.put(READ_RESTRICTION_ENABLE, withReadingRestriction); 593 594 parameters.putAll(additionalParameters); 595 596 src = _sourceResolver.resolveURI(uri, null, parameters); 597 Reader reader = new InputStreamReader(src.getInputStream(), "UTF-8"); 598 return IOUtils.toString(reader); 599 } 600 finally 601 { 602 request.setAttribute(IGNORE_RIGHT_KEY, false); 603 _sourceResolver.release(src); 604 } 605 } 606 607 /** 608 * Get the files of a user input. 609 * @param form The current form 610 * @param entry The current entry 611 * @param onlyWritableQuestion <code>true</code> to have only writable question 612 * @param onlyReadableQuestion <code>true</code> to have only readable question 613 * @return the files submitted by the user. 614 */ 615 public Collection<NamedStream> getEntryFiles(Form form, FormEntry entry, boolean onlyWritableQuestion, boolean onlyReadableQuestion) 616 { 617 Optional<Long> currentStepId = entry.getForm().hasWorkflow() ? Optional.of(_formEntryDAO.getCurrentStepId(entry)) : Optional.empty(); 618 return _formQuestionDAO.getRuleFilteredQuestions(form, new FormEntryValues(null, entry), currentStepId, onlyWritableQuestion, onlyReadableQuestion).stream() 619 .filter(q -> q.getType() instanceof FileQuestionType) 620 .map(q -> entry.<File>getValue(q.getNameForForm())) 621 .filter(Objects::nonNull) 622 .map(f -> new NamedStream(f.getInputStream(), f.getName(), f.getMimeType())) 623 .collect(Collectors.toList()); 624 } 625 626 private void _sendMail(Form form, FormEntry entry, String subject, String html, String text, String sender, String recipient, boolean addFormInformation, boolean withReadingRestriction) 627 { 628 try 629 { 630 String htmlBody = html; 631 String textBody = text; 632 633 Collection<NamedStream> records = entry != null ? getEntryFiles(form, entry, false, false) : new ArrayList<>(); 634 try 635 { 636 if (addFormInformation) 637 { 638 if (textBody.contains(_FORM_ENTRY_PATTERN)) 639 { 640 String entry2text = getMail("entry.txt", entry, Map.of(), withReadingRestriction); 641 textBody = StringUtils.replace(textBody, _FORM_ENTRY_PATTERN, entry2text); 642 } 643 644 if (htmlBody.contains(_FORM_ENTRY_PATTERN)) 645 { 646 String entry2html = getMail("entry.html", entry, Map.of(), withReadingRestriction); 647 htmlBody = StringUtils.replace(htmlBody, _FORM_ENTRY_PATTERN, entry2html); 648 } 649 } 650 651 MailBuilder mailBuilder = SendMailHelper.newMail() 652 .withAsync(true) 653 .withSubject(subject) 654 .withHTMLBody(htmlBody) 655 .withTextBody(textBody) 656 .withSender(sender) 657 .withRecipient(recipient); 658 659 if (!records.isEmpty()) 660 { 661 mailBuilder = mailBuilder.withAttachmentsAsStream(records); 662 } 663 mailBuilder.sendMail(); 664 } 665 finally 666 { 667 for (NamedStream record : records) 668 { 669 IOUtils.close(record.inputStream()); 670 } 671 } 672 } 673 catch (MessagingException | IOException e) 674 { 675 getLogger().error("Error sending the mail to " + recipient, e); 676 } 677 } 678}