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.util.HashMap; 020import java.util.List; 021import java.util.Map; 022import java.util.Objects; 023import java.util.Optional; 024 025import org.apache.avalon.framework.component.Component; 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.avalon.framework.service.Serviceable; 029import org.apache.commons.lang.StringUtils; 030 031import org.ametys.core.right.RightManager; 032import org.ametys.core.right.RightManager.RightResult; 033import org.ametys.core.ui.Callable; 034import org.ametys.core.user.User; 035import org.ametys.core.user.UserManager; 036import org.ametys.core.util.I18nUtils; 037import org.ametys.core.util.mail.SendMailHelper; 038import org.ametys.plugins.forms.dao.FormDAO; 039import org.ametys.plugins.forms.repository.Form; 040import org.ametys.plugins.repository.AmetysObjectResolver; 041import org.ametys.runtime.config.Config; 042import org.ametys.runtime.i18n.I18nizableText; 043import org.ametys.runtime.plugin.component.AbstractLogEnabled; 044import org.ametys.web.repository.page.Page; 045import org.ametys.web.repository.page.SitemapElement; 046import org.ametys.web.repository.site.Site; 047import org.ametys.web.repository.site.SiteManager; 048 049import jakarta.mail.MessagingException; 050 051/** 052 * The helper to schedule opening of form 053 */ 054public class FormInvitationsHelper extends AbstractLogEnabled implements Serviceable, Component 055{ 056 /** Avalon ROLE. */ 057 public static final String ROLE = FormInvitationsHelper.class.getName(); 058 059 /** Ametys object resolver. */ 060 protected AmetysObjectResolver _resolver; 061 062 /** The users manager */ 063 protected UserManager _userManager; 064 065 /** The site manager */ 066 protected SiteManager _siteManager; 067 068 /** The i18n utils */ 069 protected I18nUtils _i18nUtils; 070 071 /** The right manager */ 072 protected RightManager _rightManager; 073 074 /** The limited entries helper */ 075 protected LimitedEntriesHelper _limitedEntriesHelper; 076 077 /** The form DAO */ 078 protected FormDAO _formDAO; 079 080 public void service(ServiceManager manager) throws ServiceException 081 { 082 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 083 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 084 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 085 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 086 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 087 _limitedEntriesHelper = (LimitedEntriesHelper) manager.lookup(LimitedEntriesHelper.ROLE); 088 _formDAO = (FormDAO) manager.lookup(FormDAO.ROLE); 089 } 090 091 /** 092 * Sends invitations emails. 093 * @param formId The id of the form. 094 * @param message The message content. 095 * @param siteName The site name. 096 * @param language the language 097 * @return An empty map 098 */ 099 @Callable 100 public Map<String, Object> sendInvitations (String formId, String message, String siteName, String language) 101 { 102 Form form = _resolver.resolveById(formId); 103 if (_rightManager.currentUserHasRight(FormDAO.HANDLE_FORMS_RIGHT_ID, form) != RightResult.RIGHT_ALLOW) 104 { 105 throw new IllegalAccessError("Can't send invitations for form id '" + formId + "' without convenient right."); 106 } 107 108 List<Page> pages = _getFormPages(formId, siteName); 109 if (pages.isEmpty()) 110 { 111 throw new IllegalAccessError("Can't send invitations for form id '" + formId + "' because it is not published."); 112 } 113 114 String subject = _getMailSubject(language); 115 String body = _getMailBody(formId, message, siteName, language); 116 117 Site site = _siteManager.getSite(siteName); 118 String defaultFromValue = Config.getInstance().getValue("smtp.mail.from"); 119 String from = site.getValue("site-mail-from", false, defaultFromValue); 120 for (User user : _getUsersToInvite(form)) 121 { 122 try 123 { 124 String finalMessage = StringUtils.replace(body, "{name}", user.getFullName()); 125 126 SendMailHelper.newMail() 127 .withSubject(subject) 128 .withTextBody(finalMessage) 129 .withSender(from) 130 .withRecipient(user.getEmail()) 131 .sendMail(); 132 } 133 catch (MessagingException | IOException e) 134 { 135 getLogger().warn("Unable to send mail to user " + user.getEmail(), e); 136 } 137 } 138 139 return new HashMap<>(); 140 } 141 142 private List<Page> _getFormPages(String formId, String siteName) 143 { 144 List<Page> pages = _formDAO.getFormPage(formId, siteName) 145 .stream() 146 .filter(Page.class::isInstance) 147 .map(Page.class::cast) 148 .toList(); 149 return pages; 150 } 151 152 /** 153 * Get users to invite to the form (user with mail and who are not answered yet to the form) 154 * @param form the form 155 * @return the users to invite 156 */ 157 protected List<User> _getUsersToInvite(Form form) 158 { 159 // Can invite users if the form is on anonymous read access 160 if (_rightManager.hasAnonymousReadAccess(form)) 161 { 162 return List.of(); 163 } 164 165 return _rightManager.getReadAccessAllowedUsers(form).resolveAllowedUsers(true) 166 .stream() 167 .filter(id -> !_limitedEntriesHelper.hasUserAlreadyAnswer(form, id, null)) // User who are not answered yet 168 .map(id -> _userManager.getUser(id)) // Get user object 169 .filter(Objects::nonNull) // User not null 170 .filter(u -> StringUtils.isNotEmpty(u.getEmail())) // User with a mail 171 .toList(); 172 } 173 174 /** 175 * Get users to invite to the form (user with mail and who are not answered yet to the form) 176 * @param formId The id of the form. 177 * @return The number of users to invite 178 */ 179 @Callable 180 public int getNbOfUsersToInvite(String formId) 181 { 182 Form form = _resolver.resolveById(formId); 183 return _getUsersToInvite(form).size(); 184 } 185 186 /** 187 * Get the email subject 188 * @param language the language 189 * @return The subject 190 */ 191 protected String _getMailSubject (String language) 192 { 193 return _i18nUtils.translate(new I18nizableText("plugin.forms", "PLUGINS_FORMS_SEND_INVITATIONS_BOX_SUBJECT"), language); 194 } 195 196 /** 197 * Get the email body 198 * @param formId The form id 199 * @param message The message 200 * @param siteName The site name 201 * @param language the language 202 * @return The text body 203 */ 204 protected String _getMailBody (String formId, String message, String siteName, String language) 205 { 206 Site site = _siteManager.getSite(siteName); 207 String formURI = _getFormURI(formId, siteName, language); 208 209 String replacedMessage = StringUtils.replace(message, "{link}", formURI); 210 replacedMessage = StringUtils.replace(replacedMessage, "{site}", site.getTitle()); 211 212 return replacedMessage; 213 } 214 215 /** 216 * Get the form page uri 217 * @param formId The form id 218 * @param siteName The site name 219 * @param language the language 220 * @return The survey absolute uri 221 */ 222 protected String _getFormURI (String formId, String siteName, String language) 223 { 224 List<Page> pages = _getFormPages(formId, siteName); 225 226 Optional<Page> pageOptional = pages.stream() 227 .filter(p -> p.getSitemapName().equals(language)) 228 .findAny(); 229 230 SitemapElement page = pageOptional.isPresent() ? pageOptional.get() : pages.get(0); 231 Site site = _siteManager.getSite(siteName); 232 return site.getUrl() + "/" + page.getSitemap().getName() + "/" + page.getPathInSitemap() + ".html"; 233 } 234}