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.cms.search.ui.model; 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.Configurable; 026import org.apache.avalon.framework.configuration.Configuration; 027import org.apache.avalon.framework.configuration.ConfigurationException; 028import org.apache.avalon.framework.configuration.DefaultConfiguration; 029 030import org.ametys.cms.contenttype.AbstractMetadataSetElement; 031import org.ametys.cms.contenttype.ContentType; 032import org.ametys.cms.contenttype.MetadataDefinition; 033import org.ametys.cms.contenttype.MetadataDefinitionReference; 034import org.ametys.cms.contenttype.MetadataSet; 035import org.ametys.cms.contenttype.MetadataType; 036import org.ametys.cms.search.query.Query.Operator; 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.runtime.plugin.component.ThreadSafeComponentManager; 042 043/** 044 * Generic implementation of {@link SearchUIModel} for simple contents. 045 * The search tool model automatically declares simple first level metadatas as criteria and columns. 046 */ 047public class SimpleContentSearchUIModel extends AbstractSearchUIModel implements Configurable 048{ 049 050 /** ComponentManager for {@link SearchUICriterion}s. */ 051 protected ThreadSafeComponentManager<SearchUICriterion> _searchCriteriaManager; 052 053 /** ComponentManager for {@link SearchUIColumn}s. */ 054 protected ThreadSafeComponentManager<SearchUIColumn> _searchColumnManager; 055 056 /** The search criteria roles. */ 057 protected List<String> _searchCriteriaRoles; 058 059 /** The search column roles. */ 060 protected List<String> _searchColumnRoles; 061 062 @Override 063 public void configure(Configuration configuration) throws ConfigurationException 064 { 065 try 066 { 067 String cTypeId = configuration.getChild("contentType").getValue(null); 068 if (cTypeId != null) 069 { 070 setContentTypes(Collections.singleton(cTypeId)); 071 } 072 073 _searchCriteriaManager = new ThreadSafeComponentManager<>(); 074 _searchCriteriaManager.setLogger(getLogger()); 075 _searchCriteriaManager.contextualize(_context); 076 _searchCriteriaManager.service(_manager); 077 078 _searchColumnManager = new ThreadSafeComponentManager<>(); 079 _searchColumnManager.setLogger(getLogger()); 080 _searchColumnManager.contextualize(_context); 081 _searchColumnManager.service(_manager); 082 } 083 catch (Exception e) 084 { 085 throw new ConfigurationException("Unable to create local component managers.", configuration, e); 086 } 087 } 088 089 @Override 090 public Set<String> getExcludedContentTypes(Map<String, Object> contextualParameters) 091 { 092 return Collections.emptySet(); 093 } 094 095 @Override 096 public Map<String, SearchUICriterion> getCriteria(Map<String, Object> contextualParameters) 097 { 098 try 099 { 100 if (_searchCriteria == null) 101 { 102 String cTypeId = getContentTypes(contextualParameters).iterator().next(); 103 ContentType cType = _cTypeEP.getExtension(cTypeId); 104 105 _searchCriteriaRoles = new ArrayList<>(); 106 107 addCriteriaComponents(cType); 108 _searchCriteriaManager.initialize(); 109 setCriteria(getSearchUICriteria(cType)); 110 } 111 } 112 catch (Exception e) 113 { 114 throw new RuntimeException("Impossible to initialize criteria components.", e); 115 } 116 117 return _searchCriteria; 118 } 119 120 @Override 121 public Map<String, SearchUICriterion> getFacetedCriteria(Map<String, Object> contextualParameters) 122 { 123 return Collections.emptyMap(); 124 } 125 126 @Override 127 public Map<String, SearchUICriterion> getAdvancedCriteria(Map<String, Object> contextualParameters) 128 { 129 return Collections.emptyMap(); 130 } 131 132 @Override 133 public Map<String, SearchUIColumn> getResultFields(Map<String, Object> contextualParameters) 134 { 135 try 136 { 137 if (_columns == null) 138 { 139 _searchColumnRoles = new ArrayList<>(); 140 141 String cTypeId = getContentTypes(contextualParameters).iterator().next(); 142 ContentType cType = _cTypeEP.getExtension(cTypeId); 143 144 addColumnComponents(cType); 145 _searchColumnManager.initialize(); 146 setResultFields(getColumns(cType)); 147 } 148 } 149 catch (Exception e) 150 { 151 throw new RuntimeException("Impossible to initialize column components.", e); 152 } 153 154 return _columns; 155 } 156 157 /** 158 * Add criteria components to the manager. 159 * @param cType the simple content type. 160 * @throws ConfigurationException if a configuration error occurs. 161 * @throws ComponentException if a component cannot be initialized. 162 */ 163 protected void addCriteriaComponents(ContentType cType) throws ConfigurationException, ComponentException 164 { 165 MetadataSet metadataSet = cType.getMetadataSetForEdition("main"); 166 for (AbstractMetadataSetElement subMetadataSetElement : metadataSet.getElements()) 167 { 168 // Get only simple metadata (ignore composites and repeaters) 169 if (subMetadataSetElement instanceof MetadataDefinitionReference) 170 { 171 String metadataName = ((MetadataDefinitionReference) subMetadataSetElement).getMetadataName(); 172 MetadataDefinition metadataDefinition = cType.getMetadataDefinition(metadataName); 173 174 Operator operator = metadataDefinition.getType().equals(MetadataType.STRING) ? Operator.SEARCH : Operator.EQ; 175 176 addMetadataCriteriaComponent(cType, metadataName, operator); 177 } 178 } 179 180 addSystemCriteriaComponent(cType, "contributor"); 181 addSystemCriteriaComponent(cType, "contentLanguage"); 182 } 183 184 /** 185 * Add a metadata criteria component to the manager. 186 * @param contentType the simple content type. 187 * @param metadataName the metadata name. 188 * @param operator the criterion operator. 189 * @throws ConfigurationException if a configuration error occurs. 190 * @throws ComponentException if a component cannot be initialized. 191 */ 192 protected void addMetadataCriteriaComponent(ContentType contentType, String metadataName, Operator operator) throws ConfigurationException, ComponentException 193 { 194 Configuration conf = getIndexingFieldCriteriaConfiguration(contentType.getId(), metadataName, operator, null); 195 _searchCriteriaManager.addComponent("cms", null, metadataName, IndexingFieldSearchUICriterion.class, conf); 196 _searchCriteriaRoles.add(metadataName); 197 } 198 199 /** 200 * Add a system criteria component to the manager. 201 * @param contentType the simple content type. 202 * @param property the system property. 203 * @throws ConfigurationException if a configuration error occurs. 204 * @throws ComponentException if a component cannot be initialized. 205 */ 206 protected void addSystemCriteriaComponent(ContentType contentType, String property) throws ConfigurationException, ComponentException 207 { 208 DefaultConfiguration conf = (DefaultConfiguration) getSystemCriteriaConfiguration(contentType.getId(), property, null); 209 210 if (property.equals("contentLanguage")) 211 { 212 // FIXME Is this configuration should be provided by Language system property itself ? 213 // FIXME For now the simple contents are only created for language 'fr' 214 DefaultConfiguration widgetConf = new DefaultConfiguration("widget"); 215 widgetConf.setValue("edition.select-language"); 216 conf.addChild(widgetConf); 217 218 DefaultConfiguration defaultConf = new DefaultConfiguration("default-value"); 219 defaultConf.setValue("CURRENT"); 220 conf.addChild(defaultConf); 221 222 DefaultConfiguration validConf = new DefaultConfiguration("validation"); 223 DefaultConfiguration mandatoryConf = new DefaultConfiguration("mandatory"); 224 mandatoryConf.setValue(true); 225 validConf.addChild(mandatoryConf); 226 conf.addChild(validConf); 227 } 228 229 _searchCriteriaManager.addComponent("cms", null, property, SystemSearchUICriterion.class, conf); 230 _searchCriteriaRoles.add(property); 231 } 232 233 /** 234 * Lookup all the criteria. 235 * @param cType the simple content type. 236 * @return the search criteria list. 237 * @throws ComponentException if a component cannot be looked up. 238 */ 239 protected List<SearchUICriterion> getSearchUICriteria(ContentType cType) throws ComponentException 240 { 241 List<SearchUICriterion> criteria = new ArrayList<>(); 242 243 for (String role : _searchCriteriaRoles) 244 { 245 SearchUICriterion criterion = _searchCriteriaManager.lookup(role); 246 criteria.add(criterion); 247 } 248 249 return criteria; 250 } 251 252 /** 253 * Add column components to the manager. 254 * @param cType the simple content type. 255 * @throws ConfigurationException if a configuration error occurs. 256 * @throws ComponentException if a component cannot be initialized. 257 */ 258 protected void addColumnComponents(ContentType cType) throws ConfigurationException, ComponentException 259 { 260 MetadataSet metadataSet = cType.getMetadataSetForEdition("main"); 261 for (AbstractMetadataSetElement subMetadataSetElement : metadataSet.getElements()) 262 { 263 // Get only simple metadata (ignore composites and repeaters) 264 if (subMetadataSetElement instanceof MetadataDefinitionReference) 265 { 266 String metadataName = ((MetadataDefinitionReference) subMetadataSetElement).getMetadataName(); 267 268 addMetadataColumnComponent(cType, metadataName); 269 } 270 } 271 272 addSystemColumnComponent(cType, "contributor"); 273 addSystemColumnComponent(cType, "lastModified"); 274 addSystemColumnComponent(cType, "contentLanguage"); 275 } 276 277 /** 278 * Add a metadata column component to the manager. 279 * @param contentType the simple content type. 280 * @param metadataName the metadata name. 281 * @throws ConfigurationException if a configuration error occurs. 282 * @throws ComponentException if a component cannot be initialized. 283 */ 284 protected void addMetadataColumnComponent(ContentType contentType, String metadataName) throws ConfigurationException, ComponentException 285 { 286 Configuration columnConf = getMetadataColumnConfiguration(contentType.getId(), metadataName); 287 _searchColumnManager.addComponent("cms", null, metadataName, MetadataSearchUIColumn.class, columnConf); 288 _searchColumnRoles.add(metadataName); 289 } 290 291 /** 292 * Add a system column component to the manager. 293 * @param contentType the simple content type. 294 * @param property the system property. 295 * @throws ConfigurationException if a configuration error occurs. 296 * @throws ComponentException if a component cannot be initialized. 297 */ 298 protected void addSystemColumnComponent(ContentType contentType, String property) throws ConfigurationException, ComponentException 299 { 300 Configuration conf = getSystemColumnConfiguration(contentType.getId(), property); 301 _searchColumnManager.addComponent("cms", null, property, SystemSearchUIColumn.class, conf); 302 _searchColumnRoles.add(property); 303 } 304 305 /** 306 * Lookup all the columns. 307 * @param cType the simple content type. 308 * @return the search column list. 309 * @throws ComponentException if a component cannot be looked up. 310 */ 311 protected List<SearchUIColumn> getColumns(ContentType cType) throws ComponentException 312 { 313 List<SearchUIColumn> columns = new ArrayList<>(); 314 315 for (String columnRole : _searchColumnRoles) 316 { 317 SearchUIColumn column = _searchColumnManager.lookup(columnRole); 318 columns.add(column); 319 } 320 321 return columns; 322 } 323 324}