001/* 002 * Copyright 2013 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.thesaurus.search; 017 018import java.util.ArrayList; 019import java.util.Collections; 020import java.util.HashSet; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024 025import org.apache.avalon.framework.configuration.Configuration; 026import org.apache.avalon.framework.configuration.DefaultConfiguration; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.commons.lang3.ArrayUtils; 030 031import org.ametys.cms.contenttype.ContentType; 032import org.ametys.cms.contenttype.ContentTypesHelper; 033import org.ametys.cms.contenttype.MetadataDefinition; 034import org.ametys.cms.repository.Content; 035import org.ametys.cms.search.model.SystemPropertyExtensionPoint; 036import org.ametys.cms.search.query.Query.Operator; 037import org.ametys.cms.search.ui.model.AbstractSearchUIModel; 038import org.ametys.cms.search.ui.model.SearchUIColumn; 039import org.ametys.cms.search.ui.model.SearchUICriterion; 040import org.ametys.cms.search.ui.model.impl.IndexingFieldSearchUICriterion; 041import org.ametys.cms.search.ui.model.impl.MetadataSearchUIColumn; 042import org.ametys.cms.search.ui.model.impl.SystemSearchUIColumn; 043import org.ametys.cms.search.ui.model.impl.SystemSearchUICriterion; 044import org.ametys.plugins.repository.AmetysObjectResolver; 045import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 046 047/** 048 * Search model for indexed content tools 049 * 050 */ 051public class IndexedContentsSearchUIModel extends AbstractSearchUIModel 052{ 053 /** The content type helper. */ 054 protected ContentTypesHelper _cTypeHelper; 055 056 /** The system property extension point. */ 057 protected SystemPropertyExtensionPoint _sysPropEP; 058 059 /** ComponentManager for {@link SearchUICriterion}s. */ 060 protected ThreadSafeComponentManager<SearchUICriterion> _searchCriterionManager; 061 062 /** ComponentManager for {@link SearchUIColumn}s. */ 063 protected ThreadSafeComponentManager<SearchUIColumn> _searchColumnManager; 064 065 private AmetysObjectResolver _resolver; 066 067 @Override 068 public void service(ServiceManager manager) throws ServiceException 069 { 070 super.service(manager); 071 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 072 _cTypeHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 073 _sysPropEP = (SystemPropertyExtensionPoint) manager.lookup(SystemPropertyExtensionPoint.ROLE); 074 } 075 076 @Override 077 public Set<String> getContentTypes(Map<String, Object> contextualParameters) 078 { 079 Set<String> searchContentTypes = new HashSet<>(); 080 081 if (contextualParameters != null && contextualParameters.containsKey("contentId") && contextualParameters.containsKey("indexField")) 082 { 083 // Find the content type where the index field is defined 084 String contentId = (String) contextualParameters.get("contentId"); 085 Content content = _resolver.resolveById(contentId); 086 087 Set<String> cTypes = new HashSet<>(); 088 089 String[] allContentTypes = ArrayUtils.addAll(content.getTypes(), content.getMixinTypes()); 090 for (String id : allContentTypes) 091 { 092 cTypes.addAll(_cTypeHelper.getAncestors(id)); 093 cTypes.add(id); 094 } 095 096 String indexMetadataPath = (String) contextualParameters.get("indexField"); 097 for (String cTypeId : cTypes) 098 { 099 ContentType contentType = _cTypeEP.getExtension(cTypeId); 100 MetadataDefinition metadataDef = contentType.getMetadataDefinitionByPath(indexMetadataPath); 101 if (metadataDef != null) 102 { 103 searchContentTypes.add(cTypeId); 104 return searchContentTypes; 105 } 106 } 107 } 108 109 return searchContentTypes; 110 } 111 112 @Override 113 public Set<String> getExcludedContentTypes(Map<String, Object> contextualParameters) 114 { 115 return Collections.emptySet(); 116 } 117 118 @Override 119 public Map<String, SearchUICriterion> getCriteria(Map<String, Object> contextualParameters) 120 { 121 List<SearchUICriterion> criteria = new ArrayList<>(); 122 123 if (contextualParameters.containsKey("contentId")) 124 { 125 try 126 { 127 _searchCriterionManager = new ThreadSafeComponentManager<>(); 128 _searchCriterionManager.setLogger(getLogger()); 129 _searchCriterionManager.contextualize(_context); 130 _searchCriterionManager.service(_manager); 131 132 List<String> roles = new ArrayList<>(); 133 134 String contentId = (String) contextualParameters.get("contentId"); 135 136 Content content = _resolver.resolveById(contentId); 137 138 String firstCTypeId = content.getTypes()[0]; 139 140 Configuration conf = getIndexingFieldCriteriaConfiguration(firstCTypeId, "title", Operator.SEARCH); 141 _searchCriterionManager.addComponent("cms", null, "title", IndexingFieldSearchUICriterion.class, conf); 142 roles.add("title"); 143 144 conf = getSystemCriteriaConfiguration(firstCTypeId, "fulltext"); 145 _searchCriterionManager.addComponent("cms", null, "fulltext", SystemSearchUICriterion.class, conf); 146 roles.add("fulltext"); 147 148 conf = getSystemCriteriaConfiguration(firstCTypeId, "workflowStep"); 149 _searchCriterionManager.addComponent("cms", null, "workflowStep", SystemSearchUICriterion.class, conf); 150 roles.add("workflowStep"); 151 152 if (contextualParameters.containsKey("indexField") && contextualParameters.containsKey("indexValues")) 153 { 154 Set<String> cTypes = getAllTypes(content); 155 156 String indexMetadataPath = ((String) contextualParameters.get("indexField")).replaceAll("\\.", "/"); 157 @SuppressWarnings("unchecked") 158 List<String> indexMetadataValues = (List<String>) contextualParameters.get("indexValues"); 159 160 for (String cTypeId : cTypes) 161 { 162 ContentType targetContentType = _cTypeEP.getExtension(cTypeId); 163 MetadataDefinition metadataDef = targetContentType.getMetadataDefinitionByPath(indexMetadataPath); 164 if (metadataDef != null) 165 { 166 DefaultConfiguration indexFieldConf = new DefaultConfiguration("criteria"); 167 // Default value(s) 168 for (String indexMetadataValue : indexMetadataValues) 169 { 170 DefaultConfiguration defaultValueConf = new DefaultConfiguration("default-value"); 171 defaultValueConf.setValue(indexMetadataValue); 172 indexFieldConf.addChild(defaultValueConf); 173 } 174 // AND query on multiple terms 175 DefaultConfiguration multipleOperandConf = new DefaultConfiguration("multiple-operand"); 176 multipleOperandConf.setValue("and"); 177 indexFieldConf.addChild(multipleOperandConf); 178 // Hidden 179 indexFieldConf.setAttribute("hidden", "true"); 180 181 conf = getIndexingFieldCriteriaConfiguration(indexFieldConf, cTypeId, indexMetadataPath, null, null); 182 _searchCriterionManager.addComponent("cms", null, "indexField", IndexingFieldSearchUICriterion.class, conf); 183 roles.add("indexField"); 184 break; 185 } 186 } 187 } 188 189 _searchCriterionManager.initialize(); 190 191 // Lookup. 192 for (String role : roles) 193 { 194 criteria.add(_searchCriterionManager.lookup(role)); 195 } 196 197 setCriteria(criteria); 198 199 // Dispose 200 _searchCriterionManager.dispose(); 201 _searchCriterionManager = null; 202 } 203 catch (Exception e) 204 { 205 getLogger().error("Error getting the search criteria.", e); 206 throw new IllegalStateException("Error getting the search criteria.", e); 207 } 208 } 209 else 210 { 211 setCriteria(criteria); 212 } 213 214 return super.getCriteria(contextualParameters); 215 } 216 217 @Override 218 public Map<String, SearchUICriterion> getAdvancedCriteria(Map<String, Object> contextualParameters) 219 { 220 return Collections.emptyMap(); 221 } 222 223 @Override 224 public Map<String, SearchUICriterion> getFacetedCriteria(Map<String, Object> contextualParameters) 225 { 226 return Collections.emptyMap(); 227 } 228 229 @Override 230 public Map<String, SearchUIColumn> getResultFields(Map<String, Object> contextualParameters) 231 { 232 List<SearchUIColumn> columns = new ArrayList<>(); 233 234 try 235 { 236 _searchColumnManager = new ThreadSafeComponentManager<>(); 237 _searchColumnManager.setLogger(getLogger()); 238 _searchColumnManager.contextualize(_context); 239 _searchColumnManager.service(_manager); 240 241 List<String> roles = new ArrayList<>(); 242 243 if (contextualParameters.containsKey("contentId")) 244 { 245 String contentId = (String) contextualParameters.get("contentId"); 246 247 Content content = _resolver.resolveById(contentId); 248 249 String firstCTypeId = content.getTypes()[0]; 250 251 // Column title 252 Configuration conf = getMetadataColumnConfiguration(firstCTypeId, "title"); 253 _searchColumnManager.addComponent("cms", null, "title", MetadataSearchUIColumn.class, conf); 254 roles.add("title"); 255 256 if (contextualParameters.containsKey("indexField")) 257 { 258 Set<String> cTypes = getAllTypes(content); 259 260 String indexMetadataPath = ((String) contextualParameters.get("indexField")).replaceAll("\\.", "/"); 261 262 for (String cTypeId : cTypes) 263 { 264 ContentType targetContentType = _cTypeEP.getExtension(cTypeId); 265 MetadataDefinition metadataDef = targetContentType.getMetadataDefinitionByPath(indexMetadataPath); 266 if (metadataDef != null) 267 { 268 conf = getMetadataColumnConfiguration(cTypeId, indexMetadataPath); 269 _searchColumnManager.addComponent("cms", null, "indexField", MetadataSearchUIColumn.class, conf); 270 roles.add("indexField"); 271 break; 272 } 273 } 274 } 275 } 276 277 // System columns 278 for (String propertyName : _sysPropEP.getDisplayProperties()) 279 { 280 Configuration conf = getSystemColumnConfiguration(null, propertyName); 281 _searchColumnManager.addComponent("cms", null, propertyName, SystemSearchUIColumn.class, conf); 282 roles.add(propertyName); 283 } 284 285 _searchColumnManager.initialize(); 286 287 for (String role : roles) 288 { 289 columns.add(_searchColumnManager.lookup(role)); 290 } 291 292 _searchColumnManager.dispose(); 293 _searchColumnManager = null; 294 } 295 catch (Exception e) 296 { 297 getLogger().error("Error getting the result fields.", e); 298 throw new IllegalStateException("Error getting the result fields.", e); 299 } 300 301 setResultFields(columns); 302 303 return super.getResultFields(contextualParameters); 304 } 305 306 /** 307 * Get all the types and mixins and their ancestors of a Content. 308 * @param content The content. 309 * @return a Set containing all the content type and mixin IDs. 310 */ 311 protected Set<String> getAllTypes(Content content) 312 { 313 Set<String> cTypes = new HashSet<>(); 314 315 for (String id : content.getTypes()) 316 { 317 cTypes.addAll(_cTypeHelper.getAncestors(id)); 318 cTypes.add(id); 319 } 320 for (String id : content.getMixinTypes()) 321 { 322 cTypes.addAll(_cTypeHelper.getAncestors(id)); 323 cTypes.add(id); 324 } 325 326 return cTypes; 327 } 328 329}