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