MultiviewFormService.java
/*
* Copyright (c) 2002-2022, 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.forms.service;
import fr.paris.lutece.api.user.User;
import fr.paris.lutece.plugins.forms.business.Form;
import fr.paris.lutece.plugins.forms.business.FormHome;
import fr.paris.lutece.plugins.forms.business.Question;
import fr.paris.lutece.plugins.forms.business.QuestionHome;
import fr.paris.lutece.plugins.forms.business.form.FormResponseItem;
import fr.paris.lutece.plugins.forms.business.form.FormItemSortConfig;
import fr.paris.lutece.plugins.forms.business.form.column.FormColumnComparator;
import java.util.List;
import java.util.Locale;
import org.apache.commons.collections.CollectionUtils;
import fr.paris.lutece.plugins.forms.business.form.column.IFormColumn;
import fr.paris.lutece.plugins.forms.business.form.column.impl.FormColumnEntry;
import fr.paris.lutece.plugins.forms.business.form.column.impl.FormColumnEntryCartography;
import fr.paris.lutece.plugins.forms.business.form.column.impl.FormColumnEntryGeolocation;
import fr.paris.lutece.plugins.forms.business.form.filter.FormFilter;
import fr.paris.lutece.plugins.forms.business.form.filter.FormFilterForms;
import fr.paris.lutece.plugins.forms.business.form.filter.configuration.FormFilterDateConfiguration;
import fr.paris.lutece.plugins.forms.business.form.filter.configuration.FormFilterEntryConfiguration;
import fr.paris.lutece.plugins.forms.business.form.filter.configuration.FormFilterFormsConfiguration;
import fr.paris.lutece.plugins.forms.business.form.filter.configuration.IFormFilterConfiguration;
import fr.paris.lutece.plugins.forms.business.form.list.FormListFacade;
import fr.paris.lutece.plugins.forms.business.form.list.FormListLuceneDAO;
import fr.paris.lutece.plugins.forms.business.form.list.IFormListDAO;
import fr.paris.lutece.plugins.forms.business.form.panel.FormPanel;
import fr.paris.lutece.plugins.forms.business.form.search.FormResponseSearchItem;
import fr.paris.lutece.plugins.forms.util.FormsConstants;
import fr.paris.lutece.plugins.forms.web.entrytype.EntryTypeDateDisplayService;
import fr.paris.lutece.plugins.forms.web.entrytype.EntryTypeDefaultDisplayService;
import fr.paris.lutece.plugins.forms.web.entrytype.IEntryDisplayService;
import fr.paris.lutece.plugins.forms.web.form.panel.display.IFormPanelDisplay;
import fr.paris.lutece.portal.service.rbac.RBACService;
import fr.paris.lutece.portal.service.spring.SpringContextService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Service dedicated to managing of the multiview of forms
*/
public final class MultiviewFormService
{
/**
* Constructor
*/
private MultiviewFormService( )
{
}
/**
* Return the singleton of the MultiviewFormService
*
* @return the singleton of the MultiviewFormService
*/
public static MultiviewFormService getInstance( )
{
return MultiviewFormServiceHolder._singleton;
}
/**
* Populate the given FormPanel with the information from the given list of FormColumns and FormFilters
*
* @param formPanel
* The FormPanel used to retrieve the values of the FormColumns
* @param listFormColumn
* The list of all FormColumn to use to be populated
* @param listFormFilter
* The list of FormFilter to use for retrieving the data of the columns to populate
* @param nStartIndex
* The start index of doc to load
* @param nPageSize
* The size of page of docs to load
* @param sortConfig
*/
public void populateFormColumns( FormPanel formPanel, List<IFormColumn> listFormColumn, List<FormFilter> listFormFilter, int nStartIndex, int nPageSize,
FormItemSortConfig sortConfig )
{
FormListFacade formListFacade = SpringContextService.getBean( FormListFacade.BEAN_NAME );
formListFacade.populateFormColumns( formPanel, listFormColumn, listFormFilter, nStartIndex, nPageSize, sortConfig );
}
public List<FormResponseItem> searchAllListFormResponseItem( FormPanel formPanel, List<IFormColumn> listFormColumn, List<FormFilter> listFormFilter,
FormItemSortConfig sortConfig )
{
IFormListDAO formListDAO = SpringContextService.getBean( FormListLuceneDAO.BEAN_NAME );
return formListDAO.searchAllFormResponseItem( formPanel, listFormColumn, listFormFilter, sortConfig );
}
/**
* Find the FormPanel which is active in the given list
*
* @param listFormPanelDisplay
* The list to retrieve the active FormPanelDisplay
* @return the IFormFilterPanelDisplay which is active or null if not found
*/
public IFormPanelDisplay findActiveFormPanel( List<IFormPanelDisplay> listFormPanelDisplay )
{
IFormPanelDisplay formPanelDisplayActive = null;
if ( !CollectionUtils.isEmpty( listFormPanelDisplay ) )
{
for ( IFormPanelDisplay formPanelDisplay : listFormPanelDisplay )
{
if ( formPanelDisplay.isActive( ) )
{
formPanelDisplayActive = formPanelDisplay;
break;
}
}
}
return formPanelDisplayActive;
}
/**
* Holder class which manage the singleton of the MultiviewFormService
*/
private static class MultiviewFormServiceHolder
{
// Variables
private static final MultiviewFormService _singleton = new MultiviewFormService( );
}
/**
* Get the form columns list from spring and multiview conf
*
* @param nIdForm
* @return the form columns list
*/
public List<IFormColumn> getFormColumnsList( Integer nIdForm, Locale locale, User user )
{
Map<String, IFormColumn> mapFormColumns = new LinkedHashMap<>( );
// Retrieve all the column Spring beans
List<IFormColumn> listFormColumns = SpringContextService.getBeansOfType( IFormColumn.class );
Collections.sort( listFormColumns, new FormColumnComparator( ) ); // sort by position
listFormColumns.forEach( column -> mapFormColumns.put( column.getFormColumnTitle( locale ), column ) );
if ( nIdForm == null || nIdForm == FormsConstants.DEFAULT_ID_VALUE )
{
List<Form> listForm = FormHome.getFormList( );
listForm.removeIf( f -> !RBACService.isAuthorized( Form.RESOURCE_TYPE, String.valueOf( f.getId( ) ),
FormsResourceIdService.PERMISSION_VIEW_FORM_RESPONSE, user ) );
if ( listForm.size( ) == 1 )
{
nIdForm = listForm.get( 0 ).getId( );
}
}
// Then add global columns from config questions
List<Question> listQuestions = ( nIdForm == null || nIdForm == FormsConstants.DEFAULT_ID_VALUE ) ? QuestionHome.getQuestionsListUncomplete( )
: QuestionHome.getListQuestionByIdFormUncomplete( nIdForm );
// Sort questions by multiview order
listQuestions.sort( Comparator.comparing( Question::getMultiviewColumnOrder ) );
addColumnFromConfig( mapFormColumns, listQuestions, true, locale );
if ( nIdForm != null && nIdForm != FormsConstants.DEFAULT_ID_VALUE )
{
// Then add specific columns from config questions
addColumnFromConfig( mapFormColumns, listQuestions, false, locale );
}
// Filter the columns that must be displayed
mapFormColumns.entrySet( ).removeIf( entry -> !entry.getValue( ).isDisplayed( ) );
// Set the order of the columns
int nPosition = 1;
for ( IFormColumn column : mapFormColumns.values( ) )
{
column.setFormColumnPosition( nPosition++ );
}
return new ArrayList<>( mapFormColumns.values( ) );
}
/**
* Get the form columns list from spring and multiview conf
*
* @param nIdForm
* @return the form columns list
*/
public List<FormFilter> getFormFiltersList( Integer nIdForm, List<IFormColumn> listFormColumn, Locale locale, User user )
{
Map<String, FormFilter> mapFormFilter = new HashMap<>( );
// First add the XML-based filters
for ( IFormFilterConfiguration formFilterConfiguration : SpringContextService.getBeansOfType( IFormFilterConfiguration.class ) )
{
FormFilter formFilter;
if ( formFilterConfiguration instanceof FormFilterFormsConfiguration )
{
formFilter = new FormFilterForms( );
}
else
{
formFilter = new FormFilter( );
}
formFilter.setFormFilterConfiguration( formFilterConfiguration );
mapFormFilter.put( formFilter.getFormFilterConfiguration( ).getFormFilterName( ), formFilter );
}
if ( nIdForm == null || nIdForm == FormsConstants.DEFAULT_ID_VALUE )
{
List<Form> listForm = FormHome.getFormList( );
listForm.removeIf( f -> !RBACService.isAuthorized( Form.RESOURCE_TYPE, String.valueOf( f.getId( ) ),
FormsResourceIdService.PERMISSION_VIEW_FORM_RESPONSE, user ) );
if ( listForm.size( ) == 1 )
{
nIdForm = listForm.get( 0 ).getId( );
}
}
// Then add the global question-based for Filters
List<Question> listQuestions = ( nIdForm == null || nIdForm == FormsConstants.DEFAULT_ID_VALUE ) ? QuestionHome.getQuestionsListUncomplete( )
: QuestionHome.getListQuestionByIdFormUncomplete( nIdForm );
addFilterFromConfig( mapFormFilter, listQuestions, listFormColumn, true, locale );
if ( nIdForm != null && nIdForm != FormsConstants.DEFAULT_ID_VALUE )
{
// Then add specific columns from config questions
addFilterFromConfig( mapFormFilter, listQuestions, listFormColumn, false, locale );
}
return new ArrayList<>( mapFormFilter.values( ) );
}
/**
* Add the column config based on multiview config question
*
* @param mapColumns
* @param listQuestions
* @param bGlobal
*/
private void addColumnFromConfig( Map<String, IFormColumn> mapColumns, List<Question> listQuestions, boolean bGlobal, Locale locale )
{
int nPosition = mapColumns.size( );
for ( Question question : listQuestions )
{
if ( bGlobal ? question.isVisibleMultiviewGlobal( ) : question.isVisibleMultiviewFormSelected( ) )
{
question = QuestionHome.findByPrimaryKey( question.getId( ) );
if ( !mapColumns.keySet( ).contains( question.getColumnTitle( ) ) )
{
IEntryDisplayService displayService = EntryServiceManager.getInstance( ).getEntryDisplayService( question.getEntry( ).getEntryType( ) );
IFormColumn column = displayService.getFormColumn( ++nPosition, question.getColumnTitle( ) );
if (column != null)
{
addEntryCodeToColumn( column, question );
mapColumns.put( column.getFormColumnTitle( locale ), column );
}
}
else
{
IFormColumn column = mapColumns.get( question.getColumnTitle( ) );
addEntryCodeToColumn( column, question );
}
}
}
}
private void addEntryCodeToColumn( IFormColumn column, Question question )
{
if ( column instanceof FormColumnEntry )
{
( (FormColumnEntry) column ).addEntryCode( question.getCode( ) );
}
if ( column instanceof FormColumnEntryGeolocation )
{
( (FormColumnEntryGeolocation) column ).addEntryCode( question.getCode( ) );
}
if ( column instanceof FormColumnEntryCartography )
{
( (FormColumnEntryCartography) column ).addEntryCode( question.getCode( ) );
}
}
/**
* Add the filter based on multiview config question
*
* @param mapColumns
* @param listQuestions
* @param bGlobal
*/
private void addFilterFromConfig( Map<String, FormFilter> mapFilters, List<Question> listQuestions, List<IFormColumn> listFormColumns, boolean bGlobal,
Locale locale )
{
int nPosition = mapFilters.size( );
List<Question> listFiltrableQuestions = listQuestions.stream( )
.filter( question -> bGlobal ? question.isFiltrableMultiviewGlobal( ) : question.isFiltrableMultiviewFormSelected( ) )
.collect( Collectors.toList( ) );
for ( Question question : listFiltrableQuestions )
{
Question currentQuestion = QuestionHome.findByPrimaryKey( question.getId( ) );
if ( mapFilters.keySet( ).contains( currentQuestion.getCode( ) ) )
{
continue;
}
IEntryDisplayService displayService = EntryServiceManager.getInstance( ).getEntryDisplayService( currentQuestion.getEntry( ).getEntryType( ) );
if ( displayService instanceof EntryTypeDateDisplayService )
{
FormFilter formFilter = new FormFilter( );
IFormFilterConfiguration formFilterConfiguration = new FormFilterDateConfiguration( nPosition++, currentQuestion.getTitle( ),
FormResponseSearchItem.FIELD_ENTRY_CODE_SUFFIX + currentQuestion.getCode( ) + FormResponseSearchItem.FIELD_RESPONSE_FIELD_ITER + "0"
+ FormResponseSearchItem.FIELD_DATE_SUFFIX );
formFilter.setFormFilterConfiguration( formFilterConfiguration );
mapFilters.put( currentQuestion.getCode( ), formFilter );
}
if ( displayService instanceof EntryTypeDefaultDisplayService )
{
FormFilter formFilter = new FormFilter( );
List<IFormColumn> listFormColumnsEntry = listFormColumns.stream( ).filter( c -> c instanceof FormColumnEntry )
.filter( c -> c.getFormColumnTitle( locale ).equals( currentQuestion.getColumnTitle( ) ) ).collect( Collectors.toList( ) );
for ( IFormColumn column : listFormColumnsEntry )
{
IFormFilterConfiguration formFilterConfiguration = new FormFilterEntryConfiguration( nPosition++, currentQuestion.getTitle( ),
FormResponseSearchItem.FIELD_ENTRY_CODE_SUFFIX + currentQuestion.getCode( ), column );
formFilter.setFormFilterConfiguration( formFilterConfiguration );
mapFilters.put( currentQuestion.getCode( ), formFilter );
}
}
}
}
}