001/* 002 * Copyright 2026 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.util.HashMap; 019import java.util.Map; 020 021import org.apache.avalon.framework.component.Component; 022import org.apache.avalon.framework.service.ServiceException; 023import org.apache.avalon.framework.service.ServiceManager; 024import org.apache.avalon.framework.service.Serviceable; 025 026import org.ametys.cms.data.type.indexing.SortableIndexableElementType; 027import org.ametys.cms.model.properties.AbstractIndexableStaticProperty; 028import org.ametys.cms.search.model.SystemProperty; 029import org.ametys.core.user.UserManager; 030import org.ametys.plugins.forms.FormEntrySystemPropertyExtensionPoint; 031import org.ametys.plugins.forms.dao.FormEntryDAO; 032import org.ametys.plugins.forms.question.FormQuestionType; 033import org.ametys.plugins.forms.question.types.MultipleAwareQuestionType; 034import org.ametys.plugins.forms.question.types.impl.ChoicesListQuestionType; 035import org.ametys.plugins.forms.question.types.impl.FileQuestionType; 036import org.ametys.plugins.forms.question.types.impl.MatrixQuestionType; 037import org.ametys.plugins.forms.question.types.impl.RichTextQuestionType; 038import org.ametys.plugins.forms.repository.Form; 039import org.ametys.plugins.forms.repository.FormEntry; 040import org.ametys.plugins.forms.repository.FormQuestion; 041import org.ametys.runtime.model.ModelItem; 042import org.ametys.runtime.model.type.DataContext; 043import org.ametys.runtime.model.type.ModelItemType; 044import org.ametys.runtime.plugin.component.AbstractLogEnabled; 045 046/** 047 * The helper for form entry search. 048 */ 049public class FormEntriesSearchHelper extends AbstractLogEnabled implements Serviceable, Component 050{ 051 /** Avalon ROLE. */ 052 public static final String ROLE = FormEntriesSearchHelper.class.getName(); 053 054 /** The form entry DAO */ 055 protected FormEntryDAO _formEntryDAO; 056 057 /** The form entry system property extension point */ 058 protected FormEntrySystemPropertyExtensionPoint _formEntrySystemPropertyEP; 059 060 /** The user manager */ 061 protected UserManager _userManager; 062 063 public void service(ServiceManager manager) throws ServiceException 064 { 065 _formEntryDAO = (FormEntryDAO) manager.lookup(FormEntryDAO.ROLE); 066 _formEntrySystemPropertyEP = (FormEntrySystemPropertyExtensionPoint) manager.lookup(FormEntrySystemPropertyExtensionPoint.ROLE); 067 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 068 } 069 070 /** 071 * Get all form entry columns for a given form 072 * @param form the given form 073 * @return the form entry columns 074 */ 075 public Map<String, FormEntryColumn> getFormEntryColumns(Form form) 076 { 077 Map<String, FormEntryColumn> columns = new HashMap<>(); 078 079 // Then add all columns related to form entry data (questions and other model items) 080 for (ModelItem item : _formEntryDAO.getFormEntryModel(form).getModelItems()) 081 { 082 FormQuestion question = form.getQuestion(item.getName()); 083 if (question != null) 084 { 085 if (!question.getType().onlyForDisplay(question)) 086 { 087 columns.put(item.getName(), new FormEntryColumn( 088 item, 089 question, 090 _isSortable(question) ? _getSortFieldName(item) : null 091 )); 092 } 093 } 094 else if (!_isExcludedFromColumns(item)) 095 { 096 columns.put(item.getName(), new FormEntryColumn( 097 item, 098 null, 099 _getSortFieldName(item) 100 )); 101 } 102 } 103 104 return columns; 105 } 106 107 private boolean _isExcludedFromColumns(ModelItem item) 108 { 109 // Private data store in the form entry. 110 // We do not want to display it as a column 111 return item.getName().equals(FormEntry.ATTRIBUTE_ACTIVE) 112 || item.getName().equals(FormEntry.ATTRIBUTE_ANONYMIZATION_DATE) 113 || item.getName().equals(FormEntry.ATTRIBUTE_IP) 114 || item.getName().equals(FormEntry.SYSTEM_PROPERTY_WORKFLOW_ALLOWED_ACTORS) 115 || item.getName().equals(FormEntry.SYSTEM_PROPERTY_WORKFLOW_DENIED_ACTORS) 116 || item.getName().equals(FormEntry.SYSTEM_PROPERTY_ANY_CONNECTED_WORKFLOW_ACTORS) 117 || item.getName().startsWith(ChoicesListQuestionType.OTHER_PREFIX_DATA_NAME); 118 } 119 120 private boolean _isSortable(FormQuestion question) 121 { 122 FormQuestionType type = question.getType(); 123 if (type instanceof MultipleAwareQuestionType multipleType && multipleType.isMultiple(question)) 124 { 125 return false; 126 } 127 128 if (type instanceof MatrixQuestionType || type instanceof RichTextQuestionType || type instanceof FileQuestionType) 129 { 130 return false; 131 } 132 133 return true; 134 } 135 136 private String _getSortFieldName(ModelItem item) 137 { 138 // First check if the item is a system property with a specific sort field name 139 if (_formEntrySystemPropertyEP.hasExtension(item.getName())) 140 { 141 SystemProperty systemProperty = _formEntrySystemPropertyEP.getExtension(item.getName()); 142 if (systemProperty instanceof AbstractIndexableStaticProperty staticProperty) 143 { 144 return staticProperty.getSolrSortFieldName(); 145 } 146 } 147 148 // Then check if the item type is sortable and get the sort field suffix to build the sort field name 149 ModelItemType type = item.getType(); 150 if (type instanceof SortableIndexableElementType sortableType) 151 { 152 return item.getName() + sortableType.getSortFieldSuffix(DataContext.newInstance()); 153 } 154 155 return null; 156 } 157 158 /** 159 * A form entry column 160 * @param modelItem the model item 161 * @param question the form question (can be null if the model item is not a question) 162 * @param sortFieldName the sort field name (can be null if the column is not sortable) 163 */ 164 public record FormEntryColumn(ModelItem modelItem, FormQuestion question, String sortFieldName) { /* */ } 165 166}