AutomaticAssignmentService.java
/*
* Copyright (c) 2002-2021, City of Paris
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice
* and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* License 1.0
*/
package fr.paris.lutece.plugins.workflow.modules.formsautomaticassignment.service;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import fr.paris.lutece.plugins.forms.business.FormQuestionResponse;
import fr.paris.lutece.plugins.forms.business.Question;
import fr.paris.lutece.plugins.forms.business.QuestionHome;
import fr.paris.lutece.plugins.genericattributes.business.Response;
import fr.paris.lutece.plugins.genericattributes.service.entrytype.EntryTypeServiceManager;
import fr.paris.lutece.plugins.genericattributes.service.entrytype.IEntryTypeService;
import fr.paris.lutece.plugins.workflow.modules.assignment.business.AssignmentHistory;
import fr.paris.lutece.plugins.workflow.modules.assignment.business.WorkgroupConfig;
import fr.paris.lutece.plugins.workflow.modules.assignment.service.IAssignmentHistoryService;
import fr.paris.lutece.plugins.workflow.modules.assignment.service.IWorkgroupConfigService;
import fr.paris.lutece.plugins.workflow.modules.formsautomaticassignment.business.AutomaticAssignment;
import fr.paris.lutece.plugins.workflow.modules.formsautomaticassignment.business.IAutomaticAssignmentDAO;
import fr.paris.lutece.plugins.workflow.modules.formsautomaticassignment.business.TaskAutomaticAssignmentConfig;
import fr.paris.lutece.plugins.workflow.service.WorkflowPlugin;
import fr.paris.lutece.plugins.workflow.utils.WorkflowUtils;
import fr.paris.lutece.plugins.workflowcore.business.resource.ResourceHistory;
import fr.paris.lutece.plugins.workflowcore.service.config.ITaskConfigService;
import fr.paris.lutece.plugins.workflowcore.service.task.ITask;
import fr.paris.lutece.portal.business.file.File;
import fr.paris.lutece.portal.business.file.FileHome;
import fr.paris.lutece.portal.business.mailinglist.Recipient;
import fr.paris.lutece.portal.business.physicalfile.PhysicalFile;
import fr.paris.lutece.portal.business.physicalfile.PhysicalFileHome;
import fr.paris.lutece.portal.service.i18n.I18nService;
import fr.paris.lutece.portal.service.mail.MailService;
import fr.paris.lutece.portal.service.mailinglist.AdminMailingListService;
import fr.paris.lutece.portal.service.plugin.Plugin;
import fr.paris.lutece.portal.service.plugin.PluginService;
import fr.paris.lutece.portal.service.template.AppTemplateService;
import fr.paris.lutece.portal.service.util.AppPathService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
import fr.paris.lutece.util.html.HtmlTemplate;
import fr.paris.lutece.util.mail.FileAttachment;
import fr.paris.lutece.util.xml.XmlUtil;
/**
*
* AutomaticAssignmentService
*
*/
public final class AutomaticAssignmentService implements IAutomaticAssignmentService
{
public static final String BEAN_SERVICE = "workflow-formsautomaticassignment.automaticAssignmentService";
// TEMPLATE
private static final String TEMPLATE_TASK_NOTIFICATION_MAIL = "admin/plugins/workflow/modules/notification/task_notification_mail.html";
// MARKS
private static final String MARK_MESSAGE = "message";
private static final String MARK_QUESTION_MARKER = "question_";
private static final String MARK_LINK_VIEW_FORM_RESPONSE = "link_view_form_response";
// PROPERTIES
private static final String PROPERTY_LUTECE_ADMIN_PROD_URL = "lutece.admin.prod.url";
private static final String PROPERTY_LUTECE_BASE_URL = "lutece.base.url";
private static final String PROPERTY_LUTECE_PROD_URL = "lutece.prod.url";
private static final String PROPERTY_ENTRIES_TYPE_ALLOWED = "workflow-forms-automatic-assignment.entriesTypeAllowed";
private static final String PROPERTY_ENTRIES_TYPE_FILES = "workflow-forms-automatic-assignment.entriesTypeFiles";
// MESSAGES
private static final String PROPERTY_MAIL_SENDER_NAME = "module.workflow.assignment.task_assignment_config.mailSenderName";
// JSP
private static final String PROPERTY_VIEW_RECAP_URL = "workflow-forms-automatic-assignment.viewRecapUrl";
// TAGS
private static final String TAG_A = "a";
// ATTRIBUTES
private static final String ATTRIBUTE_HREF = "href";
// CONSTANTS
private static final String COMMA = ",";
// CONSTANTS
private static final String CONSTANT_COMMA = ", ";
private static final String CONSTANT_SLASH = "/";
// SERVICES
@Inject
private IAutomaticAssignmentDAO _automaticAssignmentDAO;
@Inject
@Named( TaskAutomaticAssignmentConfigService.BEAN_SERVICE )
private ITaskConfigService _taskAutomaticAssignmentConfigService;
@Inject
private IAutomaticAssignmentService _automaticAssignmentService;
@Inject
private IAssignmentHistoryService _assignmentHistoryService;
@Inject
private IWorkgroupConfigService _workgroupConfigService;
/**
* Private constructor
*/
private AutomaticAssignmentService( )
{
}
/**
* {@inheritDoc}
*/
@Override
@Transactional( AutomaticAssignmentPlugin.BEAN_TRANSACTION_MANAGER )
public void create( AutomaticAssignment assign, Plugin plugin )
{
_automaticAssignmentDAO.insert( assign, plugin );
}
/**
* {@inheritDoc}
*/
@Override
@Transactional( AutomaticAssignmentPlugin.BEAN_TRANSACTION_MANAGER )
public void remove( AutomaticAssignment assign, Plugin plugin )
{
_automaticAssignmentDAO.delete( assign, plugin );
}
/**
* {@inheritDoc}
*/
@Override
@Transactional( AutomaticAssignmentPlugin.BEAN_TRANSACTION_MANAGER )
public void removeByTask( int nIdTask, Plugin plugin )
{
_automaticAssignmentDAO.deleteByTask( nIdTask, plugin );
}
// CHECKS
/**
* {@inheritDoc}
*/
@Override
public boolean checkExist( AutomaticAssignment assign, Plugin plugin )
{
return _automaticAssignmentDAO.checkExist( assign, plugin );
}
// GET
/**
* {@inheritDoc}
*/
@Override
public List<AutomaticAssignment> findByTaskByQuestion( int nIdTask, int nIdEntry, Plugin plugin )
{
return _automaticAssignmentDAO.loadByTaskByQuestion( nIdTask, nIdEntry, plugin );
}
/**
* {@inheritDoc}
*/
@Override
public List<AutomaticAssignment> findByTask( int nIdTask, Plugin plugin )
{
return _automaticAssignmentDAO.loadByTask( nIdTask, plugin );
}
/**
* {@inheritDoc}
*/
@Override
public List<Integer> findAllIdEntriesByTask( int nIdTask, Plugin plugin )
{
return _automaticAssignmentDAO.getIdQuestionsListByTask( nIdTask, plugin );
}
/**
* {@inheritDoc}
*/
@Override
public List<Question> getAllQuestions( int nIdTask )
{
TaskAutomaticAssignmentConfig config = _taskAutomaticAssignmentConfigService.findByPrimaryKey( nIdTask );
List<Question> listQuestions = new ArrayList<>( );
if ( config != null )
{
listQuestions = QuestionHome.getListQuestionByIdForm( config.getIdForm( ) );
}
return listQuestions;
}
/**
* {@inheritDoc}
*/
@Override
public List<Question> getAuthorizedQuestions( int nIdTask )
{
List<Integer> listIdTypesAuthorized = fillListEntryTypes( PROPERTY_ENTRIES_TYPE_ALLOWED );
return getAllQuestions( nIdTask ).stream( ).filter( x -> listIdTypesAuthorized.contains( x.getEntry( ).getEntryType( ).getIdType( ) ) )
.collect( Collectors.toList( ) );
}
@Override
public List<Question> getQuestionsTypesFile( int nIdTask )
{
List<Integer> listIdTypesFiles = fillListEntryTypes( PROPERTY_ENTRIES_TYPE_FILES );
return getAllQuestions( nIdTask ).stream( ).filter( x -> listIdTypesFiles.contains( x.getEntry( ).getEntryType( ).getIdType( ) ) )
.collect( Collectors.toList( ) );
}
/**
* {@inheritDoc}
*/
@Override
public List<FileAttachment> getFilesAttachment( TaskAutomaticAssignmentConfig config, List<FormQuestionResponse> listFormQuestionResponse )
{
List<FileAttachment> listFileAttachment = new ArrayList<>( );
if ( CollectionUtils.isEmpty( config.getListPositionsQuestionFile( ) ) )
{
return listFileAttachment;
}
for ( Integer nPositionEntryFile : config.getListPositionsQuestionFile( ) )
{
List<File> listFiles = getFiles( nPositionEntryFile, listFormQuestionResponse );
if ( CollectionUtils.isNotEmpty( listFiles ) )
{
for ( File file : listFiles )
{
if ( ( file != null ) && ( file.getPhysicalFile( ) != null ) )
{
FileAttachment fileAttachment = new FileAttachment( file.getTitle( ), file.getPhysicalFile( ).getValue( ), file.getMimeType( ) );
listFileAttachment.add( fileAttachment );
}
}
}
}
return listFileAttachment;
}
/**
* {@inheritDoc}
*/
@Override
public void notify( List<FormQuestionResponse> listFormQuestionResponse, TaskAutomaticAssignmentConfig config, List<String> listWorkgroup,
ResourceHistory resourceHistory, HttpServletRequest request, Locale locale, ITask task )
{
Plugin workflowPlugin = PluginService.getPlugin( WorkflowPlugin.PLUGIN_NAME );
String strSenderEmail = MailService.getNoReplyEmail( );
String strSenderName = config.getSenderName( );
if ( StringUtils.isBlank( strSenderName ) )
{
strSenderName = I18nService.getLocalizedString( PROPERTY_MAIL_SENDER_NAME, locale );
}
Map<String, Object> model = buildModel( config, resourceHistory, listFormQuestionResponse, request, locale );
String strEmailContent = buildMailHtml( model, locale );
String strSubject = buildSubjectHtml( config, model, locale );
List<FileAttachment> listFileAttachments = getFilesAttachment( config, listFormQuestionResponse );
// Notify the mailings list associated to each workgroup
for ( String workGroup : listWorkgroup )
{
// add history
AssignmentHistory history = new AssignmentHistory( );
history.setIdResourceHistory( resourceHistory.getId( ) );
history.setIdTask( task.getId( ) );
history.setWorkgroup( workGroup );
_assignmentHistoryService.create( history, workflowPlugin );
WorkgroupConfig workgroupConfig = _workgroupConfigService.findByPrimaryKey( task.getId( ), workGroup, workflowPlugin );
if ( workgroupConfig == null || workgroupConfig.getIdMailingList( ) == WorkflowUtils.CONSTANT_ID_NULL )
{
continue;
}
Collection<Recipient> listRecipients = AdminMailingListService.getRecipients( workgroupConfig.getIdMailingList( ) );
// Send Mail
for ( Recipient recipient : listRecipients )
{
if ( CollectionUtils.isNotEmpty( listFileAttachments ) )
{
MailService.sendMailMultipartHtml( recipient.getEmail( ), null, null, strSenderName, strSenderEmail, strSubject, strEmailContent, null,
listFileAttachments );
}
else
{
// Build the mail message
MailService.sendMailHtml( recipient.getEmail( ), strSenderName, strSenderEmail, strSubject, strEmailContent );
}
}
}
// Notify recipients
boolean bHasRecipients = ( StringUtils.isNotBlank( config.getRecipientsBcc( ) ) || StringUtils.isNotBlank( config.getRecipientsCc( ) ) );
if ( !bHasRecipients )
{
return;
}
if ( CollectionUtils.isNotEmpty( listFileAttachments ) )
{
MailService.sendMailMultipartHtml( null, config.getRecipientsCc( ), config.getRecipientsBcc( ), strSenderName, strSenderEmail, strSubject,
strEmailContent, null, listFileAttachments );
}
else
{
MailService.sendMailHtml( null, config.getRecipientsCc( ), config.getRecipientsBcc( ), config.getSenderName( ), strSenderEmail, strSubject,
strEmailContent );
}
}
// PRIVATE METHODS
/**
* Fill the list of entry types
*
* @param strPropertyEntryTypes
* the property containing the entry types
* @return a list of integer
*/
private static List<Integer> fillListEntryTypes( String strPropertyEntryTypes )
{
List<Integer> listEntryTypes = new ArrayList<>( );
String strEntryTypes = AppPropertiesService.getProperty( strPropertyEntryTypes );
if ( StringUtils.isNotBlank( strEntryTypes ) )
{
String [ ] listAcceptEntryTypesForIdDemand = strEntryTypes.split( COMMA );
for ( String strAcceptEntryType : listAcceptEntryTypesForIdDemand )
{
if ( StringUtils.isNotBlank( strAcceptEntryType ) && StringUtils.isNumeric( strAcceptEntryType ) )
{
int nAcceptedEntryType = Integer.parseInt( strAcceptEntryType );
listEntryTypes.add( nAcceptedEntryType );
}
}
}
return listEntryTypes;
}
/**
* Get the directory files
*
* @param nPosition
* the position of the entry
* @param nIdRecord
* the id record
* @param nIdDirectory
* the id directory
* @return the directory file
*/
private List<File> getFiles( int nPosition, List<FormQuestionResponse> listFormQuestionResponse )
{
List<File> listFiles = new ArrayList<>( );
for ( FormQuestionResponse formQuestionResponse : listFormQuestionResponse )
{
if ( formQuestionResponse.getQuestion( ).getEntry( ).getPosition( ) == nPosition
&& !CollectionUtils.isEmpty( formQuestionResponse.getEntryResponse( ) ) )
{
File file = formQuestionResponse.getEntryResponse( ).get( 0 ).getFile( );
if ( file != null )
{
file = FileHome.findByPrimaryKey( file.getIdFile( ) );
PhysicalFile physicalFile = PhysicalFileHome.findByPrimaryKey( file.getPhysicalFile( ).getIdPhysicalFile( ) );
file.setPhysicalFile( physicalFile );
listFiles.add( file );
}
}
}
return listFiles;
}
/**
* Get the base url
*
* @param request
* the HTTP request
* @return the base url
*/
private String getBaseUrl( HttpServletRequest request )
{
String strBaseUrl;
if ( request != null )
{
strBaseUrl = AppPathService.getBaseUrl( request );
}
else
{
strBaseUrl = AppPropertiesService.getProperty( PROPERTY_LUTECE_ADMIN_PROD_URL );
if ( StringUtils.isBlank( strBaseUrl ) )
{
strBaseUrl = AppPropertiesService.getProperty( PROPERTY_LUTECE_BASE_URL );
if ( StringUtils.isBlank( strBaseUrl ) )
{
strBaseUrl = AppPropertiesService.getProperty( PROPERTY_LUTECE_PROD_URL );
}
}
}
return strBaseUrl;
}
/**
* Build the mail Html
*
* @param model
* the model
* @param locale
* the {@link Locale}
* @return the mail HTML
*/
private String buildMailHtml( Map<String, Object> model, Locale locale )
{
HtmlTemplate t = AppTemplateService
.getTemplateFromStringFtl( AppTemplateService.getTemplate( TEMPLATE_TASK_NOTIFICATION_MAIL, locale, model ).getHtml( ), locale, model );
return t.getHtml( );
}
/**
* Build the subject Html
*
* @param config
* the config
* @param model
* the model
* @param locale
* the {@link Locale}
* @return the subject
*/
private String buildSubjectHtml( TaskAutomaticAssignmentConfig config, Map<String, Object> model, Locale locale )
{
return AppTemplateService.getTemplateFromStringFtl( config.getSubject( ), locale, model ).getHtml( );
}
/**
* Build the model for the mail content and for the subject
*
* @param config
* the config
* @param resourceHistory
* the resource history
* @param request
* the HTTP request
* @param locale
* the {@link Locale}
* @return the model
*/
private Map<String, Object> buildModel( TaskAutomaticAssignmentConfig config, ResourceHistory resourceHistory,
List<FormQuestionResponse> listFormQuestionResponse, HttpServletRequest request, Locale locale )
{
Map<String, Object> model = new HashMap<>( );
List<Question> allQuestions = _automaticAssignmentService.getAllQuestions( config.getIdTask( ) );
// Get values for markers that can be used in the message
for ( Question questionAuthorized : allQuestions )
{
String strKey = MARK_QUESTION_MARKER + questionAuthorized.getId( );
for ( FormQuestionResponse formQuestionResponse : listFormQuestionResponse )
{
if ( formQuestionResponse.getQuestion( ).getId( ) == questionAuthorized.getId( ) )
{
IEntryTypeService entryTypeService = EntryTypeServiceManager.getEntryTypeService( questionAuthorized.getEntry( ) );
for ( Response response : formQuestionResponse.getEntryResponse( ) )
{
if ( response.getFile( ) != null )
{
response.setFile( FileHome.findByPrimaryKey( response.getFile( ).getIdFile( ) ) );
}
String strQuestionResponseValue = entryTypeService.getResponseValueForRecap( formQuestionResponse.getQuestion( ).getEntry( ), request,
response, locale );
if ( model.containsKey( strKey ) )
{
model.put( strKey, model.get( strKey ) + CONSTANT_COMMA + strQuestionResponseValue );
}
else
{
model.put( strKey, strQuestionResponseValue );
}
}
}
}
}
// Link View record
String strLinkViewRecordHtml = "";
if ( config.isViewFormResponse( ) )
{
StringBuilder sRecapUrl = new StringBuilder( getBaseUrl( request ) );
if ( ( sRecapUrl.length( ) > 0 ) && !sRecapUrl.toString( ).endsWith( CONSTANT_SLASH ) )
{
sRecapUrl.append( CONSTANT_SLASH );
}
sRecapUrl.append( AppPropertiesService.getProperty( PROPERTY_VIEW_RECAP_URL ) );
MessageFormat mrecapUrl = new MessageFormat( sRecapUrl.toString( ) );
Object [ ] recapParams = {
config.getIdForm( ), resourceHistory.getIdResource( )
};
String strFinalUrl = mrecapUrl.format( recapParams );
StringBuffer sbLinkHtml = new StringBuffer( );
Map<String, String> mapParams = new HashMap<>( );
mapParams.put( ATTRIBUTE_HREF, strFinalUrl );
XmlUtil.beginElement( sbLinkHtml, TAG_A, mapParams );
sbLinkHtml.append( config.getLabelLinkViewRecord( ) );
XmlUtil.endElement( sbLinkHtml, TAG_A );
Map<String, Object> modelTmp = new HashMap<>( );
modelTmp.put( MARK_LINK_VIEW_FORM_RESPONSE, strFinalUrl );
strLinkViewRecordHtml = AppTemplateService.getTemplateFromStringFtl( sbLinkHtml.toString( ), locale, modelTmp ).getHtml( );
}
model.put( MARK_LINK_VIEW_FORM_RESPONSE, strLinkViewRecordHtml );
model.put( MARK_MESSAGE, config.getMessage( ) );
return model;
}
}