001/*
002 *  Copyright 2018 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.workspaces.members;
017
018import java.io.UnsupportedEncodingException;
019import java.net.URLEncoder;
020import java.util.ArrayList;
021import java.util.Arrays;
022import java.util.Date;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027
028import javax.jcr.RepositoryException;
029import javax.mail.MessagingException;
030
031import org.apache.avalon.framework.component.Component;
032import org.apache.avalon.framework.configuration.Configurable;
033import org.apache.avalon.framework.configuration.Configuration;
034import org.apache.avalon.framework.configuration.ConfigurationException;
035import org.apache.avalon.framework.context.Context;
036import org.apache.avalon.framework.context.ContextException;
037import org.apache.avalon.framework.context.Contextualizable;
038import org.apache.avalon.framework.service.ServiceException;
039import org.apache.avalon.framework.service.ServiceManager;
040import org.apache.avalon.framework.service.Serviceable;
041import org.apache.cocoon.components.ContextHelper;
042import org.apache.cocoon.environment.Request;
043import org.apache.commons.lang3.StringUtils;
044
045import org.ametys.cms.transformation.xslt.ResolveURIComponent;
046import org.ametys.core.ui.Callable;
047import org.ametys.core.user.CurrentUserProvider;
048import org.ametys.core.user.User;
049import org.ametys.core.user.UserIdentity;
050import org.ametys.core.user.UserManager;
051import org.ametys.core.user.directory.NotUniqueUserException;
052import org.ametys.core.user.population.PopulationContextHelper;
053import org.ametys.core.util.I18nUtils;
054import org.ametys.core.util.mail.SendMailHelper;
055import org.ametys.plugins.core.user.UserHelper;
056import org.ametys.plugins.repository.AmetysObjectIterable;
057import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector;
058import org.ametys.plugins.workspaces.members.MembersWorkspaceModule.Invitation;
059import org.ametys.plugins.workspaces.project.ProjectManager;
060import org.ametys.plugins.workspaces.project.modules.WorkspaceModuleExtensionPoint;
061import org.ametys.plugins.workspaces.project.objects.Project;
062import org.ametys.plugins.workspaces.project.rights.ProjectRightHelper;
063import org.ametys.runtime.i18n.I18nizableText;
064import org.ametys.runtime.plugin.component.AbstractLogEnabled;
065import org.ametys.runtime.plugin.component.PluginAware;
066import org.ametys.web.repository.page.Page;
067import org.ametys.web.repository.page.Zone;
068import org.ametys.web.repository.page.ZoneItem;
069import org.ametys.web.repository.page.ZoneItem.ZoneType;
070import org.ametys.web.repository.site.Site;
071import org.ametys.web.repository.site.SiteManager;
072import org.ametys.web.usermanagement.UserManagementException;
073import org.ametys.web.usermanagement.UserSignUpConfiguration;
074import org.ametys.web.usermanagement.UserSignupManager;
075
076import com.google.common.collect.Iterables;
077
078/**
079 * Helper for invitations by email
080 *
081 */
082public class ProjectInvitationHelper extends AbstractLogEnabled implements Serviceable, Component, Configurable, PluginAware, Contextualizable
083{
084    /** The role */
085    public static final String ROLE = ProjectInvitationHelper.class.getName();
086    
087    private static final String __MAIL_PROJECT_EMAIL_PATTERN = "${email}";
088    private static final String __MAIL_PROJECT_TOKEN_PATTERN = "${token}";
089    
090    private ProjectManager _projectManager;
091    private UserSignUpConfiguration _signupConfig;
092    private UserSignupManager _signupManager;
093    private SiteManager _siteManager;
094    private WorkspaceModuleExtensionPoint _moduleEP;
095    private CurrentUserProvider _currentUserProvider;
096    private UserManager _userManager;
097    private UserHelper _userHelper;
098    private ProjectRightHelper _projectRightsHelper;
099    private ProjectMemberManager _projectMemberManager;
100    private PopulationContextHelper _populationContextHelper;
101
102    private String _subjectKeyForInvitation;
103    private String _textBodyKeyForInvitation;
104    private String _htmlBodyKeyForInvitation;
105    private String _subjectKeyForInvitationAccepted;
106    private String _textBodyKeyForInvitationAccepted;
107    private String _htmlBodyKeyForInvitationAccepted;
108
109    private I18nUtils _i18nUtils;
110
111    private String _pluginName;
112
113    private Context _context;
114
115
116    public void setPluginInfo(String pluginName, String featureName, String id)
117    {
118        _pluginName = pluginName;
119    }
120    
121    public void contextualize(Context context) throws ContextException
122    {
123        _context = context;
124    }
125    
126    @Override
127    public void service(ServiceManager serviceManager) throws ServiceException
128    {
129        _projectManager = (ProjectManager) serviceManager.lookup(ProjectManager.ROLE);
130        _projectRightsHelper = (ProjectRightHelper) serviceManager.lookup(ProjectRightHelper.ROLE);
131        _projectMemberManager = (ProjectMemberManager) serviceManager.lookup(ProjectMemberManager.ROLE);
132        _signupConfig = (UserSignUpConfiguration) serviceManager.lookup(UserSignUpConfiguration.ROLE);
133        _signupManager = (UserSignupManager) serviceManager.lookup(UserSignupManager.ROLE);
134        _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE);
135        _moduleEP = (WorkspaceModuleExtensionPoint) serviceManager.lookup(WorkspaceModuleExtensionPoint.ROLE);
136        _currentUserProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
137        _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE);
138        _userHelper = (UserHelper) serviceManager.lookup(UserHelper.ROLE);
139        _i18nUtils = (I18nUtils) serviceManager.lookup(I18nUtils.ROLE);
140        _populationContextHelper = (PopulationContextHelper) serviceManager.lookup(PopulationContextHelper.ROLE);
141    }
142    
143    @Override
144    public void configure(Configuration configuration) throws ConfigurationException
145    {
146        _subjectKeyForInvitation = configuration.getChild("invitation-email-subject").getValue(null);
147        _textBodyKeyForInvitation = configuration.getChild("invitation-email-text-body").getValue(null);
148        _htmlBodyKeyForInvitation = configuration.getChild("invitation-email-html-body").getValue(null);
149        
150        _subjectKeyForInvitationAccepted = configuration.getChild("invitation-accepted-email-subject").getValue(null);
151        _textBodyKeyForInvitationAccepted = configuration.getChild("invitation-accepted-email-text-body").getValue(null);
152        _htmlBodyKeyForInvitationAccepted = configuration.getChild("invitation-accepted-email-html-body").getValue(null);
153    }
154    
155    
156    /**
157     * Invite emails to be member of a project
158     * @param projectName The project name
159     * @param siteName The site name
160     * @param lang the current language
161     * @param emails The emails
162     * @param allowedProfileByModule the allowed profiles by module
163     * @param mailSubject The subject of mail
164     * @param mailBody The body of mail
165     * @return The result
166     * @throws UserManagementException if failed to invit user
167     * @throws NotUniqueUserException if many users match the given email
168     * @throws IllegalAccessException If the user cannot execute this operation
169     */
170    @Callable
171    public Map<String, Object> inviteEmails(String projectName, String siteName, String lang, List<String> emails, Map<String, String> allowedProfileByModule, String mailSubject, String mailBody) throws UserManagementException, IllegalAccessException, NotUniqueUserException
172    {
173        Map<String, Object> result = new HashMap<>();
174        
175        result.put("existing-users", new ArrayList<Map<String, Object>>());
176        result.put("email-success", new ArrayList<String>());
177        result.put("email-error", new ArrayList<String>());
178        
179        Project project = _projectManager.getProject(projectName);
180        if (project != null)
181        {
182            MembersWorkspaceModule module = _moduleEP.getModule(MembersWorkspaceModule.MEMBERS_MODULE_ID);
183            if (module != null && _projectManager.isModuleActivated(project, module.getId()))
184            {
185                if (!_projectRightsHelper.canAddMember(project))
186                {
187                    throw new IllegalAccessException("User '" + _currentUserProvider.getUser() + "' tried to send invitations without sufficient right."); 
188                }
189                
190                String catalogSiteName = _projectManager.getCatalogSiteName();
191                
192                String userDirectoryAsStr = _getUserDirectoryForSignup(result, catalogSiteName, lang);
193                if (userDirectoryAsStr != null)
194                {
195                    String populationId = StringUtils.substringBeforeLast(userDirectoryAsStr, "#");
196                    String userDirectoryId = StringUtils.substringAfterLast(userDirectoryAsStr, "#");
197                    
198                    for (String email : emails)
199                    {
200                        try
201                        {
202                            if (!_signupManager.userExists(email, catalogSiteName))
203                            {
204                                _signupManager.temporarySignup(siteName, lang, email, populationId, userDirectoryId);
205                                
206                                // Whatever or not the mail has already been invited, re-created the invitation
207                                if (_addOrUpdateInvitation(project, catalogSiteName, module, email, allowedProfileByModule, populationId, userDirectoryId, mailSubject, mailBody))
208                                {
209                                    @SuppressWarnings("unchecked")
210                                    List<String> emailSuccess = (List<String>) result.get("email-success");
211                                    emailSuccess.add(email);
212                                    
213                                }
214                                else
215                                {
216                                    @SuppressWarnings("unchecked")
217                                    List<String> emailErrors = (List<String>) result.get("email-error");
218                                    emailErrors.add(email);
219                                }
220                            }
221                            else
222                            {
223                                User user = _getUser(catalogSiteName, email);
224                                
225                                Map<String, Object> user2json = _userHelper.user2json(user, true);
226                                
227                                @SuppressWarnings("unchecked")
228                                List<Map<String, Object>> existingUsers = (List<Map<String, Object>>) result.get("existing-users");
229                                existingUsers.add(user2json);
230                            }
231                        }
232                        catch (UserManagementException e)
233                        {
234                            getLogger().error("Cannot invite " + email, e);
235                            @SuppressWarnings("unchecked")
236                            List<String> emailErrors = (List<String>) result.get("email-error");
237                            emailErrors.add(email);
238                        }
239                    }
240                    
241                    @SuppressWarnings("unchecked")
242                    List<String> emailSuccess = (List<String>) result.get("email-success");
243                    if (emailSuccess.size() == emails.size())
244                    {
245                        result.put("success", true);
246                    }
247                }
248            }
249        }
250        else
251        {
252            result.put("success", false);
253            result.put("unknown-project", projectName);
254        }
255        
256        return result;
257    }
258    
259    private User _getUser(String siteName, String email) throws NotUniqueUserException
260    {
261        Set<String> populations = _populationContextHelper.getUserPopulationsOnContexts(Arrays.asList("/sites/" + siteName, "/sites-fo/" + siteName), false);
262        for (String population : populations)
263        {
264            User user = _userManager.getUser(population, email);
265            if (user == null)
266            {
267                user = _userManager.getUserByEmail(population, email);
268            }
269            
270            if (user != null)
271            {
272                return user;
273            }
274        }
275        
276        return null;
277    }
278    
279    /**
280     * Get the configuration to invite users by emails
281     * @param projectName The current project
282     * @param lang the current language
283     * @return the configuration for email invitations
284     */
285    @Callable
286    public Map<String, Object> getInvitationConfiguration(String projectName, String lang)
287    {
288        Project project = _projectManager.getProject(projectName);
289        
290        Map<String, Object> config = new HashMap<>();
291        
292        // Check the configuration is valid for invitations
293        String catalogSiteName = _projectManager.getCatalogSiteName();
294        if (_getUserDirectoryForSignup(config, catalogSiteName, lang) == null)
295        {
296            config.remove("success");
297            config.put("allowed", false); 
298            return config;
299        }
300        
301        // Check the current user has right to invite users
302        if (!_projectRightsHelper.canAddMember(project))
303        {
304            config.put("allowed", false); 
305            config.put("error", "no-right"); 
306            return config;
307        }
308        
309        config.put("allowed", true);
310        config.put("email", _getInvitationEmailConfiguration(project, lang));
311        config.put("rights", _projectRightsHelper.getProjectRightsData(projectName)); 
312        
313        return config;
314    }
315    
316    private Map<String, Object> _getInvitationEmailConfiguration(Project project, String lang)
317    {
318        Map<String, Object> emailConfig = new HashMap<>();
319        
320        Map<String, I18nizableText> i18nparams = new HashMap<>();
321        i18nparams.put("projectTitle", new I18nizableText(project.getTitle()));
322        i18nparams.put("projectUrl", new I18nizableText(Iterables.getFirst(_projectManager.getProjectUrls(project), StringUtils.EMPTY)));
323        i18nparams.put("nbDays", new I18nizableText(String.valueOf(_signupConfig.getTokenValidity())));
324        i18nparams.put("nbDays", new I18nizableText(String.valueOf(_signupConfig.getTokenValidity())));
325        
326        String catalogSiteName = _projectManager.getCatalogSiteName();
327        Site catalogSite = _siteManager.getSite(catalogSiteName);
328        i18nparams.put("catalogSiteTitle", new I18nizableText(catalogSite.getTitle()));
329        i18nparams.put("catalogSiteUrl", new I18nizableText(catalogSite.getUrl()));
330        
331        Page signupPage = _signupManager.getSignupPage(_projectManager.getCatalogSiteName(), lang);
332        if (signupPage != null)
333        {
334            String signupUri = ResolveURIComponent.resolve("page", signupPage.getId(), false, true) + "?email=${email}&token=${token}";
335            i18nparams.put("signupUri", new I18nizableText(signupUri));
336        }
337        
338        String subject = getSubjectForInvitationEmail(i18nparams, lang);
339        if (subject != null)
340        {
341            emailConfig.put("subject", subject);
342        }
343        String bodyTxt = getTextBodyForInvitationEmail(i18nparams, lang);
344        if (bodyTxt != null)
345        {
346            emailConfig.put("bodyText", bodyTxt);
347        }
348        String bodyHtml = getHtmlBodyForInvitationEmail(i18nparams, lang);
349        if (bodyHtml != null)
350        {
351            emailConfig.put("bodyHtml", bodyHtml);
352        }
353        
354        return emailConfig;
355    }
356    
357    private void _sendInvitationMail (String catalogSiteName, String email, String populationId, String userDirectoryId, String mailSubject, String mailBodyTpl) throws UserManagementException
358    {
359        String bodyTxt = mailBodyTpl;
360        
361        String encodedEmail;
362        try
363        {
364            encodedEmail = URLEncoder.encode(email, "UTF-8");
365        }
366        catch (UnsupportedEncodingException e)
367        {
368            // Should never happen.
369            throw new UserManagementException("Encoding error while sending a sign-up confirmation e-mail.", e);
370        }
371
372        String token = _signupManager.getToken(catalogSiteName, email, populationId, userDirectoryId);
373        
374        bodyTxt = StringUtils.replace(bodyTxt, __MAIL_PROJECT_TOKEN_PATTERN, token);
375        bodyTxt = StringUtils.replace(bodyTxt, __MAIL_PROJECT_EMAIL_PATTERN, encodedEmail);
376        
377        getLogger().debug("Sending signup invitation e-mail to {}", email);
378        
379        Site site = _siteManager.getSite(catalogSiteName);
380        String from = site.getValue("site-mail-from");
381
382        try
383        {
384            // Send the e-mail.
385            SendMailHelper.sendMail(mailSubject, null, bodyTxt, email, from);
386        }
387        catch (MessagingException e)
388        {
389            throw new UserManagementException("Error sending the sign-up confirmation mail.", e);
390        }
391    }
392    
393    private boolean _addOrUpdateInvitation(Project project, String catalogSiteName, MembersWorkspaceModule module, String email, Map<String, String> allowedProfileByModules, String populationId, String userDirectoryId, String mailSubject, String mailBody) throws UserManagementException
394    {
395        try
396        {
397            // First remove invitation if exists
398            module.removeInvitation(project, email);
399            
400            // Add invitations with temporary rights
401            module.addInvitation(project, new Date(), email, _currentUserProvider.getUser(), allowedProfileByModules);
402            
403            project.saveChanges();
404
405            _sendInvitationMail(catalogSiteName, email, populationId, userDirectoryId, mailSubject, mailBody);
406            
407            return true;
408        }
409        catch (RepositoryException e)
410        {
411            getLogger().error("Fail to store invitation for email " + email, e);
412            return false;
413        }
414    }
415    
416    private String _getUserDirectoryForSignup(Map<String, Object> result, String catalogSiteName, String lang)
417    {
418        if (catalogSiteName == null)
419        {
420            getLogger().error("The catalog's site name is not configured. User invitations can not be activated.");
421            result.put("success", false);
422            result.put("error", "invalid-configuration");
423            return null;
424        }
425        
426        Page signupPage = _signupManager.getSignupPage(catalogSiteName, lang);
427        if (signupPage == null)
428        {
429            getLogger().error("The catalog's site does not contain the signup service for language " + lang + ". User invitations can not be activated.");
430            result.put("success", false);
431            result.put("error", "invalid-configuration");
432            return null;
433        }
434        
435        String userDirectoryAsStr = _getUserDirectoryForSignup(signupPage);
436        if (StringUtils.isEmpty(userDirectoryAsStr))
437        {
438            getLogger().error("There is no user directory configured for users signup. Please check the sign up service of catalog's site.");
439            result.put("success", false);
440            result.put("error", "invalid-configuration");
441            return null;
442        }
443        
444        return userDirectoryAsStr;
445            
446    }
447    
448    private String _getUserDirectoryForSignup(Page signupPage)
449    {
450        for (Zone zone : signupPage.getZones())
451        {
452            for (ZoneItem zoneItem : zone.getZoneItems())
453            {
454                if (zoneItem.getType() == ZoneType.SERVICE && zoneItem.getServiceId().equals("org.ametys.web.service.UserSignup"))
455                {
456                    return zoneItem.getServiceParameters().getValue("userdirectory");
457                }
458            }
459        }
460        
461        return null;
462        
463    }
464    
465    /**
466     * Add user as member of all project where it has been invited
467     * @param user the new user
468     */
469    public void createMemberFromInvitations(User user)
470    {
471        Request request = ContextHelper.getRequest(_context);
472        String currentWorkspace = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
473        
474        try
475        {
476            // Force default workspace
477            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, "default");
478            
479            MembersWorkspaceModule memberModule = (MembersWorkspaceModule) _moduleEP.getExtension(MembersWorkspaceModule.MEMBERS_MODULE_ID);
480            
481            List<Invitation> invitations = memberModule.getInvitations(user.getEmail());
482            
483            for (Invitation invitation : invitations)
484            {
485                Map<String, String> allowedProfileByModules = invitation.getAllowedProfileByModules();
486                String projectName = invitation.getProjectName();
487                
488                Project project = _projectManager.getProject(projectName);
489                
490                // Create new member
491                JCRProjectMember member = _projectMemberManager.getOrCreateProjectMember(project, user.getIdentity());
492                
493                // Set member's rights
494                _projectMemberManager.setMemberProfiles(allowedProfileByModules, member, project, true);
495                
496                project.saveChanges();
497                
498                // Notify author that invitation was accepted
499                UserIdentity author = invitation.getAuthor();
500                sendInvitationAcceptedMail(project, _userManager.getUser(author), user);
501                
502                try
503                {
504                    // Remove invitation
505                    memberModule.removeInvitation(project, invitation.getEmail());
506                }
507                catch (RepositoryException e)
508                {
509                    getLogger().error("Failed to remove invitation " + invitation, e);
510                }
511            }
512        }
513        finally 
514        {
515            // Restore current workspace
516            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWorkspace);
517        }
518    
519    }
520    
521    /**
522     * Send email to the user who initiated the invitation
523     * @param project The project
524     * @param invitAuthor The author of invitation
525     * @param newMember The new member
526     */
527    protected void sendInvitationAcceptedMail(Project project, User invitAuthor, User newMember)
528    {
529        if (invitAuthor != null)
530        {
531            Site site = project.getSites().iterator().next();
532            String from = site.getValue("site-mail-from");
533            String email = invitAuthor.getEmail();
534            
535            if (StringUtils.isNotEmpty(email))
536            {
537                // Prepare mail.
538                Map<String, I18nizableText> i18nParams = new HashMap<>();
539                i18nParams.put("projectTitle", new I18nizableText(project.getTitle()));
540                i18nParams.put("projectUrl", new I18nizableText(Iterables.getFirst(_projectManager.getProjectUrls(project), StringUtils.EMPTY)));
541                i18nParams.put("newMember", new I18nizableText(newMember.getFullName()));
542                i18nParams.put("newMemberMail", new I18nizableText(newMember.getEmail()));
543                
544                AmetysObjectIterable<Page> memberPages = _projectManager.getModulePages(project, MembersWorkspaceModule.MEMBERS_MODULE_ID, null);
545                if (memberPages.getSize() > 0)
546                {
547                    Page page = memberPages.iterator().next();
548                    i18nParams.put("membersPageUri", new I18nizableText(ResolveURIComponent.resolve("page", page.getId(), false, true)));
549                }
550
551                String subject = getSubjectForInvitationAcceptedEmail(i18nParams, null);
552                String textBody = getTextBodyForInvitationAcceptedEmail(i18nParams, null);
553                String htmlBody = getHtmlBodyForInvitationAcceptedEmail(i18nParams, null);
554
555                try
556                {
557                    // Send the e-mail.
558                    SendMailHelper.sendMail(subject, htmlBody, textBody, email, from);
559                }
560                catch (MessagingException e)
561                {
562                    getLogger().error("Error sending the invitation accepted email.", e);
563                }
564            }
565        }
566    }
567    
568    /**
569     * The email subject for invitation by email
570     * @param defaultI18nParams The default i18n parameters
571     * @param language the language
572     * @return the email subject for invitation by email
573     */
574    public String getSubjectForInvitationEmail (Map<String, I18nizableText> defaultI18nParams , String language)
575    {
576        if (StringUtils.isNotBlank(_subjectKeyForInvitation))
577        {
578            return _i18nUtils.translate(_getI18nizableText(_subjectKeyForInvitation, defaultI18nParams), language);
579        }
580        
581        return null;
582    }
583
584    /**
585     * The email text body for invitation by email
586     * @param defaultI18nParams The default i18n parameters with :
587     * siteName the site name
588     * email the mail
589     * token the token
590     * confirmUri the confirmation uri
591     * siteTitle the site title
592     * siteUrl the site url
593     * @param language the language
594     * @return the email text for invitation by email
595     */
596    public String getTextBodyForInvitationEmail(Map<String, I18nizableText> defaultI18nParams, String language)
597    {
598        if (StringUtils.isNotBlank(_textBodyKeyForInvitation))
599        {
600            return _i18nUtils.translate(_getI18nizableText(_textBodyKeyForInvitation, defaultI18nParams), language);
601        }
602        
603        return null;
604    }
605
606    /**
607     * The email html body for invitation by email
608     * @param defaultI18nParams The default i18n parameters with :
609     * siteName the site name
610     * email the mail
611     * token the token
612     * confirmUri the confirmation uri
613     * siteTitle the site title
614     * siteUrl the site url
615     * @param language the language
616     * @return the email html for invitation by email
617     */
618    public String getHtmlBodyForInvitationEmail(Map<String, I18nizableText> defaultI18nParams, String language)
619    {
620        if (StringUtils.isNotBlank(_htmlBodyKeyForInvitation))
621        {
622            return _i18nUtils.translate(_getI18nizableText(_htmlBodyKeyForInvitation, defaultI18nParams), language);
623        }
624        
625        return null;
626    }
627    
628    /**
629     * The email subject for invitation by email
630     * @param defaultI18nParams The default i18n parameters
631     * @param language the language
632     * @return the email subject for invitation by email
633     */
634    public String getSubjectForInvitationAcceptedEmail (Map<String, I18nizableText> defaultI18nParams , String language)
635    {
636        if (StringUtils.isNotBlank(_subjectKeyForInvitationAccepted))
637        {
638            return _i18nUtils.translate(_getI18nizableText(_subjectKeyForInvitationAccepted, defaultI18nParams), language);
639        }
640        
641        return null;
642    }
643    
644    /**
645     * The email text body for invitation by email
646     * @param defaultI18nParams The default i18n parameters with :
647     * @param language the language
648     * @return the email text for invitation by email
649     */
650    public String getTextBodyForInvitationAcceptedEmail(Map<String, I18nizableText> defaultI18nParams, String language)
651    {
652        if (StringUtils.isNotBlank(_textBodyKeyForInvitationAccepted))
653        {
654            return _i18nUtils.translate(_getI18nizableText(_textBodyKeyForInvitationAccepted, defaultI18nParams), language);
655        }
656        
657        return null;
658    }
659    
660    /**
661     * The email html body for invitation by email
662     * @param defaultI18nParams The default i18n parameters with :
663     * @param language the language
664     * @return the email html for invitation by email
665     */
666    public String getHtmlBodyForInvitationAcceptedEmail(Map<String, I18nizableText> defaultI18nParams, String language)
667    {
668        if (StringUtils.isNotBlank(_htmlBodyKeyForInvitationAccepted))
669        {
670            return _i18nUtils.translate(_getI18nizableText(_htmlBodyKeyForInvitationAccepted, defaultI18nParams), language);
671        }
672        
673        return null;
674    }
675    
676    /**
677     * Get the {@link I18nizableText} from the configured key and i18n parameters
678     * @param fullI18nKey the configured i18n key
679     * @param i18nParams the i18n parameters
680     * @return the i18nizable text
681     */
682    protected I18nizableText _getI18nizableText(String fullI18nKey, Map<String, I18nizableText> i18nParams)
683    {
684        String catalogue = StringUtils.contains(fullI18nKey, ":") ? StringUtils.substringBefore(fullI18nKey, ":") : "plugin." + _pluginName;
685        String i18nKey = StringUtils.contains(fullI18nKey, ":") ? StringUtils.substringAfter(fullI18nKey, ":") : fullI18nKey;
686        
687        return new I18nizableText(catalogue, i18nKey, i18nParams);
688    }
689    
690}