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