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.cms.content.referencetable.search; 017 018import java.util.ArrayList; 019import java.util.Collections; 020import java.util.List; 021import java.util.Map; 022import java.util.Set; 023 024import org.apache.avalon.framework.component.ComponentException; 025import org.apache.avalon.framework.configuration.Configuration; 026import org.apache.avalon.framework.configuration.ConfigurationException; 027import org.apache.avalon.framework.configuration.DefaultConfiguration; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030 031import org.ametys.cms.repository.Content; 032import org.ametys.cms.search.query.Query.Operator; 033import org.ametys.cms.search.ui.model.AbstractSearchUIModel; 034import org.ametys.cms.search.ui.model.SearchUIColumn; 035import org.ametys.cms.search.ui.model.SearchUICriterion; 036import org.ametys.cms.search.ui.model.SearchUIModel; 037import org.ametys.cms.search.ui.model.impl.IndexingFieldSearchUICriterion; 038import org.ametys.cms.search.ui.model.impl.MetadataSearchUIColumn; 039import org.ametys.cms.search.ui.model.impl.SystemSearchUIColumn; 040import org.ametys.cms.search.ui.model.impl.SystemSearchUICriterion; 041import org.ametys.plugins.repository.AmetysObjectResolver; 042import org.ametys.runtime.model.ModelItem; 043import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 044 045/** 046 * Implementation of {@link SearchUIModel} for contents referencing the same content's values for a given metadata. 047 */ 048public class ReferencingContentsWithSameValuesSearchUIModel extends AbstractSearchUIModel 049{ 050 /** ComponentManager for {@link SearchUICriterion}s. */ 051 protected ThreadSafeComponentManager<SearchUICriterion> _searchCriterionManager; 052 053 /** ComponentManager for {@link SearchUIColumn}s. */ 054 protected ThreadSafeComponentManager<SearchUIColumn> _searchColumnManager; 055 /** The search column roles. */ 056 protected List<String> _searchColumnRoles; 057 058 private AmetysObjectResolver _resolver; 059 060 @Override 061 public void service(ServiceManager manager) throws ServiceException 062 { 063 super.service(manager); 064 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 065 } 066 067 @Override 068 public Set<String> getContentTypes(Map<String, Object> contextualParameters) 069 { 070 if (contextualParameters != null && contextualParameters.containsKey("contentId") && contextualParameters.containsKey("referenceField")) 071 { 072 // Get the content type which has declared the attribute 073 String contentId = (String) contextualParameters.get("contentId"); 074 Content content = _resolver.resolveById(contentId); 075 076 String referenceAttributePath = (String) contextualParameters.get("referenceField"); 077 if (content.hasDefinition(referenceAttributePath)) 078 { 079 ModelItem modelItem = content.getDefinition(referenceAttributePath); 080 return Set.of(modelItem.getModel().getId()); 081 } 082 } 083 084 return Set.of(); 085 } 086 087 @Override 088 public Set<String> getExcludedContentTypes(Map<String, Object> contextualParameters) 089 { 090 return Collections.emptySet(); 091 } 092 093 @Override 094 public Map<String, SearchUICriterion> getCriteria(Map<String, Object> contextualParameters) 095 { 096 List<SearchUICriterion> criteria = new ArrayList<>(); 097 098 if (contextualParameters.containsKey("contentId")) 099 { 100 try 101 { 102 _searchCriterionManager = new ThreadSafeComponentManager<>(); 103 _searchCriterionManager.setLogger(getLogger()); 104 _searchCriterionManager.contextualize(_context); 105 _searchCriterionManager.service(_manager); 106 107 List<String> roles = new ArrayList<>(); 108 109 String contentId = (String) contextualParameters.get("contentId"); 110 111 Content content = _resolver.resolveById(contentId); 112 113 Set<String> setWithOnlyFirstCTypeId = Set.of(content.getTypes()[0]); 114 115 Configuration conf = getIndexingFieldCriteriaConfiguration(setWithOnlyFirstCTypeId, Content.ATTRIBUTE_TITLE, Operator.SEARCH); 116 _searchCriterionManager.addComponent("cms", null, Content.ATTRIBUTE_TITLE, IndexingFieldSearchUICriterion.class, conf); 117 roles.add("title"); 118 119 conf = getSystemCriteriaConfiguration(setWithOnlyFirstCTypeId, "fulltext"); 120 _searchCriterionManager.addComponent("cms", null, "fulltext", SystemSearchUICriterion.class, conf); 121 roles.add("fulltext"); 122 123 conf = getSystemCriteriaConfiguration(setWithOnlyFirstCTypeId, "workflowStep"); 124 _searchCriterionManager.addComponent("cms", null, "workflowStep", SystemSearchUICriterion.class, conf); 125 roles.add("workflowStep"); 126 127 if (contextualParameters.containsKey("referenceField") && contextualParameters.containsKey("referenceValues")) 128 { 129 String referenceMetadataPath = ((String) contextualParameters.get("referenceField")).replaceAll("\\.", "/"); 130 131 if (content.hasDefinition(referenceMetadataPath)) 132 { 133 ModelItem modelItem = content.getDefinition(referenceMetadataPath); 134 @SuppressWarnings("unchecked") 135 List<String> referenceAttributeValues = (List<String>) contextualParameters.get("referenceValues"); 136 137 DefaultConfiguration referenceFieldConf = new DefaultConfiguration("criteria"); 138 // Default value(s) 139 for (String referenceAttributeValue : referenceAttributeValues) 140 { 141 DefaultConfiguration defaultValueConf = new DefaultConfiguration("default-value"); 142 defaultValueConf.setValue(referenceAttributeValue); 143 referenceFieldConf.addChild(defaultValueConf); 144 } 145 // AND query on multiple entries 146 DefaultConfiguration multipleOperandConf = new DefaultConfiguration("multiple-operand"); 147 multipleOperandConf.setValue("and"); 148 referenceFieldConf.addChild(multipleOperandConf); 149 // Hidden 150 referenceFieldConf.setAttribute("hidden", "true"); 151 152 conf = getIndexingFieldCriteriaConfiguration(referenceFieldConf, Set.of(modelItem.getModel().getId()), referenceMetadataPath, null, null); 153 _searchCriterionManager.addComponent("cms", null, "referenceField", IndexingFieldSearchUICriterion.class, conf); 154 roles.add("referenceField"); 155 } 156 } 157 158 _searchCriterionManager.initialize(); 159 160 // Lookup. 161 for (String role : roles) 162 { 163 criteria.add(_searchCriterionManager.lookup(role)); 164 } 165 166 setCriteria(criteria); 167 168 // Dispose 169 _searchCriterionManager.dispose(); 170 _searchCriterionManager = null; 171 } 172 catch (Exception e) 173 { 174 getLogger().error("Error getting the search criteria.", e); 175 throw new IllegalStateException("Error getting the search criteria.", e); 176 } 177 } 178 else 179 { 180 setCriteria(criteria); 181 } 182 183 return super.getCriteria(contextualParameters); 184 } 185 186 @Override 187 public Map<String, SearchUICriterion> getAdvancedCriteria(Map<String, Object> contextualParameters) 188 { 189 return Collections.emptyMap(); 190 } 191 192 @Override 193 public Map<String, SearchUICriterion> getFacetedCriteria(Map<String, Object> contextualParameters) 194 { 195 return Collections.emptyMap(); 196 } 197 198 @Override 199 public Map<String, SearchUIColumn> getResultFields(Map<String, Object> contextualParameters) 200 { 201 List<SearchUIColumn> columns = new ArrayList<>(); 202 203 try 204 { 205 _searchColumnManager = new ThreadSafeComponentManager<>(); 206 _searchColumnManager.setLogger(getLogger()); 207 _searchColumnManager.contextualize(_context); 208 _searchColumnManager.service(_manager); 209 210 _searchColumnRoles = new ArrayList<>(); 211 212 if (contextualParameters.containsKey("contentId")) 213 { 214 String contentId = (String) contextualParameters.get("contentId"); 215 216 Content content = _resolver.resolveById(contentId); 217 218 String firstCTypeId = content.getTypes()[0]; 219 220 // Column title 221 Configuration conf = getMetadataColumnConfiguration(Set.of(firstCTypeId), Content.ATTRIBUTE_TITLE); 222 _searchColumnManager.addComponent("cms", null, Content.ATTRIBUTE_TITLE, MetadataSearchUIColumn.class, conf); 223 _searchColumnRoles.add(Content.ATTRIBUTE_TITLE); 224 225 if (contextualParameters.containsKey("referenceField")) 226 { 227 String referenceAttributePath = ((String) contextualParameters.get("referenceField")).replaceAll("\\.", "/"); 228 if (content.hasDefinition(referenceAttributePath)) 229 { 230 ModelItem modelItem = content.getDefinition(referenceAttributePath); 231 conf = getMetadataColumnConfiguration(Set.of(modelItem.getModel().getId()), referenceAttributePath); 232 _searchColumnManager.addComponent("cms", null, "referenceField", MetadataSearchUIColumn.class, conf); 233 _searchColumnRoles.add("referenceField"); 234 } 235 } 236 } 237 238 addSystemColumnComponent("contributor"); 239 addSystemColumnComponent("lastModified"); 240 addSystemColumnComponent("contentTypes"); 241 addSystemColumnComponent("contentLanguage"); 242 addSystemColumnComponent("workflowStep"); 243 244 _searchColumnManager.initialize(); 245 246 for (String role : _searchColumnRoles) 247 { 248 columns.add(_searchColumnManager.lookup(role)); 249 } 250 251 _searchColumnManager.dispose(); 252 _searchColumnManager = null; 253 } 254 catch (Exception e) 255 { 256 getLogger().error("Error getting the result fields.", e); 257 throw new IllegalStateException("Error getting the result fields.", e); 258 } 259 260 setResultFields(columns); 261 262 return super.getResultFields(contextualParameters); 263 } 264 265 /** 266 * Add a system column component to the manager. 267 * @param property the system property. 268 * @throws ConfigurationException if a configuration error occurs. 269 * @throws ComponentException if a component cannot be initialized. 270 */ 271 protected void addSystemColumnComponent(String property) throws ConfigurationException, ComponentException 272 { 273 Configuration conf = getSystemColumnConfiguration(null, property); 274 _searchColumnManager.addComponent("cms", null, property, SystemSearchUIColumn.class, conf); 275 _searchColumnRoles.add(property); 276 } 277 278}