001/*
002 *  Copyright 2015 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.Collection;
019import java.util.Collections;
020import java.util.HashSet;
021import java.util.LinkedHashMap;
022import java.util.Map;
023import java.util.Set;
024
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.context.Context;
029import org.apache.avalon.framework.context.ContextException;
030import org.apache.avalon.framework.context.Contextualizable;
031import org.apache.avalon.framework.service.ServiceException;
032import org.apache.avalon.framework.service.ServiceManager;
033import org.apache.avalon.framework.service.Serviceable;
034import org.slf4j.Logger;
035
036import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
037import org.ametys.cms.search.model.SearchCriterion;
038import org.ametys.cms.search.query.Query.Operator;
039import org.ametys.runtime.i18n.I18nizableText;
040import org.ametys.runtime.plugin.component.LogEnabled;
041
042/**
043 * Abstract class for SearchUIModel.
044 */
045public abstract class AbstractSearchUIModel implements SearchUIModel, LogEnabled, Serviceable, Contextualizable
046{
047    /** The content type extension point */
048    protected ContentTypeExtensionPoint _cTypeEP;
049    /** The search model helper. */
050    protected SearchUIModelHelper _searchModelHelper;
051    /** The logger. */
052    protected Logger _logger;
053    /** The service manager */
054    protected ServiceManager _manager;
055    /** The context. */
056    protected Context _context;
057    
058    /** The content types of this search model. */
059    protected Set<String> _cTypes;
060    /** The content types excluded from this search model. */
061    protected Set<String> _excludedCTypes;
062    
063    /** The search criteria in simple mode, indexed by ID. */
064    protected Map<String, SearchUICriterion> _searchCriteria;
065    /** The search criteria in advanced mode, indexed by ID. */
066    protected Map<String, SearchUICriterion> _advancedSearchCriteria;
067    /** The search criteria used as facets, indexed by ID. */
068    protected Map<String, SearchUICriterion> _facetedCriteria;
069    /** The result columns, indexed by ID. */
070    protected Map<String, SearchUIColumn> _columns;
071    
072    public void setLogger(final Logger logger)
073    {
074        _logger = logger;
075    }
076    
077    /**
078     * Get the logger.
079     * @return the logger.
080     */
081    protected final Logger getLogger()
082    {
083        return _logger;
084    }
085    
086    @Override
087    public void contextualize(Context context) throws ContextException
088    {
089        _context = context;
090    }
091    
092    @Override
093    public void service(ServiceManager manager) throws ServiceException
094    {
095        _cTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE);
096        _searchModelHelper = (SearchUIModelHelper) manager.lookup(SearchUIModelHelper.ROLE);
097        _manager = manager;
098    }
099    
100    @Override
101    public Set<String> getContentTypes(Map<String, Object> contextualParameters)
102    {
103        return Collections.unmodifiableSet(_cTypes);
104    }
105    
106    /**
107     * Set the content types.
108     * @param cTypes The content types.
109     */
110    public void setContentTypes(Set<String> cTypes)
111    {
112        _cTypes = new HashSet<>(cTypes);
113    }
114    
115    @Override
116    public Set<String> getExcludedContentTypes(Map<String, Object> contextualParameters)
117    {
118        return Collections.unmodifiableSet(_excludedCTypes);
119    }
120    
121    /**
122     * Set the excluded content types.
123     * @param cTypes The excluded content types.
124     */
125    public void setExcludedContentTypes(Set<String> cTypes)
126    {
127        _excludedCTypes = new HashSet<>(cTypes);
128    }
129    
130    @Override
131    public Map<String, SearchUICriterion> getCriteria(Map<String, Object> contextualParameters)
132    {
133        return Collections.unmodifiableMap(_searchCriteria);
134    }
135    
136    /**
137     * Set the criteria in simple mode.
138     * @param criteria A collection of search criteria.
139     */
140    public void setCriteria(Collection<SearchUICriterion> criteria)
141    {
142        _searchCriteria = new LinkedHashMap<>();
143        for (SearchCriterion criterion : criteria)
144        {
145            _searchCriteria.put(criterion.getId(), (SearchUICriterion) criterion);
146        }
147    }
148    
149    @Override
150    public Map<String, SearchUICriterion> getAdvancedCriteria(Map<String, Object> contextualParameters)
151    {
152        return Collections.unmodifiableMap(_advancedSearchCriteria);
153    }
154    
155    /**
156     * Set the criteria in advanced mode.
157     * @param criteria A collection of search criteria.
158     */
159    public void setAdvancedCriteria(Collection<SearchUICriterion> criteria)
160    {
161        _advancedSearchCriteria = new LinkedHashMap<>();
162        for (SearchUICriterion criterion : criteria)
163        {
164            _advancedSearchCriteria.put(criterion.getId(), criterion);
165        }
166    }
167    
168    @Override
169    public Map<String, SearchUICriterion> getFacetedCriteria(Map<String, Object> contextualParameters)
170    {
171        return Collections.unmodifiableMap(_facetedCriteria);
172    }
173    
174    /**
175     * Set the criteria to use as facets.
176     * @param criteria A collection of search criteria.
177     */
178    public void setFacetedCriteria(Collection<SearchCriterion> criteria)
179    {
180        _facetedCriteria = new LinkedHashMap<>();
181        for (SearchCriterion criterion : criteria)
182        {
183            _facetedCriteria.put(criterion.getId(), (SearchUICriterion) criterion);
184        }
185    }
186    
187    @Override
188    public Map<String, SearchUIColumn> getResultFields(Map<String, Object> contextualParameters)
189    {
190        return Collections.unmodifiableMap(_columns);
191    }
192    
193    /**
194     * Set the result columns.
195     * @param fields A collection of search columns.
196     */
197    public void setResultFields(Collection<SearchUIColumn> fields)
198    {
199        _columns = new LinkedHashMap<>();
200        for (SearchUIColumn column : fields)
201        {
202            _columns.put(column.getId(), column);
203        }
204    }
205    
206    @Override
207    public int getPageSize(Map<String, Object> contextualParameters)
208    {
209        // Use the default value or unlimited.
210        return -1;
211    }
212    
213    @Override
214    public String getWorkspace(Map<String, Object> contextualParameters)
215    {
216        // Use the default workspace.
217        return null;
218    }
219    
220    @Override
221    public String getSearchUrl(Map<String, Object> contextualParameters)
222    {
223        return "search/list.json";
224    }
225
226    @Override
227    public String getSearchUrlPlugin(Map<String, Object> contextualParameters)
228    {
229        return "cms";
230    }
231
232    @Override
233    public String getExportCSVUrl(Map<String, Object> contextualParameters)
234    {
235        return "search/export.csv";
236    }
237
238    @Override
239    public String getExportCSVUrlPlugin(Map<String, Object> contextualParameters)
240    {
241        return "cms";
242    }
243
244    @Override
245    public String getExportXMLUrl(Map<String, Object> contextualParameters)
246    {
247        return "search/export.xml";
248    }
249
250    @Override
251    public String getExportXMLUrlPlugin(Map<String, Object> contextualParameters)
252    {
253        return "cms";
254    }
255
256    @Override
257    public String getPrintUrl(Map<String, Object> contextualParameters)
258    {
259        return "search/print.html";
260    }
261
262    @Override
263    public String getPrintUrlPlugin(Map<String, Object> contextualParameters)
264    {
265        return "cms";
266    }
267    
268    /**
269     * Get the configuration of a metadata criteria component.
270     * @param contentTypeId the content type ID.
271     * @param path the field path.
272     * @param operator the criteria operator, can be null.
273     * @return the configuration to provide to the metadata criteria component.
274     * @throws ConfigurationException if an error occurs.
275     */
276    protected Configuration getIndexingFieldCriteriaConfiguration(String contentTypeId, String path, Operator operator) throws ConfigurationException
277    {
278        return getIndexingFieldCriteriaConfiguration(contentTypeId, path, operator, null);
279    }
280    
281    /**
282     * Get the configuration of a metadata criteria component.
283     * @param contentTypeId the content type ID.
284     * @param path the field path.
285     * @param operator the criteria operator, can be null.
286     * @param group The group. Can ne null.
287     * @return the configuration to provide to the metadata criteria component.
288     * @throws ConfigurationException if an error occurs.
289     */
290    protected Configuration getIndexingFieldCriteriaConfiguration(String contentTypeId, String path, Operator operator, I18nizableText group) throws ConfigurationException
291    {
292        return getIndexingFieldCriteriaConfiguration(new DefaultConfiguration("criteria"), contentTypeId, path, operator, group);
293    }
294    
295    /**
296     * Get the configuration of a metadata criteria component.
297     * @param originalConf the original criteria configuration.
298     * @param contentTypeId the content type ID.
299     * @param path the field path, separated by '/'.
300     * @param operator the criteria operator, can be null.
301     * @param group The group. Can ne null.
302     * @return the configuration to provide to the metadata criterion component.
303     * @throws ConfigurationException if an error occurs.
304     */
305    protected Configuration getIndexingFieldCriteriaConfiguration(Configuration originalConf, String contentTypeId, String path, Operator operator, I18nizableText group) throws ConfigurationException
306    {
307        DefaultConfiguration conf = new DefaultConfiguration(originalConf);
308        
309        DefaultConfiguration metaConf = new DefaultConfiguration("field");
310        conf.addChild(metaConf);
311        metaConf.setAttribute("path", path);
312        
313        if (operator != null)
314        {
315            DefaultConfiguration opConf = new DefaultConfiguration("test-operator");
316            conf.addChild(opConf);
317            opConf.setValue(operator.getName());
318        }
319        
320        addContentTypesConfiguration(conf, contentTypeId);
321        
322        if (group != null)
323        {
324            DefaultConfiguration groupConf = new DefaultConfiguration("group");
325            groupConf.setAttribute("i18n", group.isI18n());
326            groupConf.setValue(group.isI18n() ? group.getCatalogue() + ":" + group.getKey() : group.getLabel());
327            conf.addChild(groupConf);
328        }
329        
330        return conf;
331    }
332    
333    /**
334     * Get the configuration of a system criteria component.
335     * @param contentTypeId the content type ID.
336     * @param property the system property.
337     * @return the configuration to provide to the system criterion component.
338     */
339    protected Configuration getSystemCriteriaConfiguration(String contentTypeId, String property)
340    {
341        return getSystemCriteriaConfiguration(contentTypeId, property, null);
342    }
343    
344    /**
345     * Get the configuration of a system criteria component.
346     * @param contentTypeId the content type ID.
347     * @param property the system property.
348     * @param group The group. Can ne null.
349     * @return the configuration to provide to the system criterion component.
350     */
351    protected Configuration getSystemCriteriaConfiguration(String contentTypeId, String property, I18nizableText group)
352    {
353        try
354        {
355            return getSystemCriteriaConfiguration(new DefaultConfiguration("criteria"), contentTypeId, property, group);
356        }
357        catch (ConfigurationException e)
358        {
359            // Ignore, just can't happen.
360            return null;
361        }
362    }
363    
364    /**
365     * Get the configuration of a system criteria component.
366     * @param originalConf the original column configuration.
367     * @param contentTypeId the content type ID.
368     * @param property the system property.
369     * @param group The group. Can ne null.
370     * @return the configuration to provide to the system criterion component.
371     * @throws ConfigurationException if an error occurs.
372     */
373    protected Configuration getSystemCriteriaConfiguration(Configuration originalConf, String contentTypeId, String property, I18nizableText group) throws ConfigurationException
374    {
375        DefaultConfiguration conf = new DefaultConfiguration(originalConf);
376        
377        DefaultConfiguration propConf = new DefaultConfiguration("systemProperty");
378        propConf.setAttribute("name", property);
379        conf.addChild(propConf);
380        
381        addContentTypesConfiguration(conf, contentTypeId);
382        
383        if (group != null)
384        {
385            DefaultConfiguration groupConf = new DefaultConfiguration("group");
386            groupConf.setAttribute("i18n", group.isI18n());
387            groupConf.setValue(group.isI18n() ? group.getCatalogue() + ":" + group.getKey() : group.getLabel());
388            conf.addChild(groupConf);
389        }
390        
391        return conf;
392    }
393    
394    /**
395     * Get the configuration of a custom criteria component.
396     * @param originalConf the original column configuration.
397     * @param contentTypeId the content type ID.
398     * @param customCriterionId the custom criterion id
399     * @param group The group. Can be null.
400     * @return the configuration to provide to the custom criterion component.
401     * @throws ConfigurationException if an error occurs.
402     */
403    protected Configuration getCustomCriteriaConfiguration(Configuration originalConf, String contentTypeId, String customCriterionId, I18nizableText group) throws ConfigurationException
404    {
405        DefaultConfiguration conf = new DefaultConfiguration(originalConf);
406        
407        DefaultConfiguration customCriterionConf = new DefaultConfiguration("customCriterion");
408        customCriterionConf.setAttribute("id", customCriterionId);
409        conf.addChild(customCriterionConf);
410        
411        addContentTypesConfiguration(conf, contentTypeId);
412        
413        if (group != null)
414        {
415            DefaultConfiguration groupConf = new DefaultConfiguration("group");
416            groupConf.setAttribute("i18n", group.isI18n());
417            groupConf.setValue(group.isI18n() ? group.getCatalogue() + ":" + group.getKey() : group.getLabel());
418            conf.addChild(groupConf);
419        }
420        
421        return conf;
422    }
423
424    /**
425     * Get the configuration of a metadata column component.
426     * @param contentTypeId the content type ID.
427     * @param metadataPath the metadata path.
428     * @return the configuration to provide to the metadata column component.
429     * @throws ConfigurationException if an error occurs.
430     */
431    protected Configuration getMetadataColumnConfiguration(String contentTypeId, String metadataPath) throws ConfigurationException
432    {
433        return getMetadataColumnConfiguration(new DefaultConfiguration("column"), contentTypeId, metadataPath);
434    }
435    
436    /**
437     * Get the configuration of a metadata column component.
438     * @param originalConf the original column configuration.
439     * @param contentTypeId the content type ID, may be null.
440     * @param metadataPath the metadata path.
441     * @return the configuration to provide to the metadata column component.
442     * @throws ConfigurationException if an error occurs.
443     */
444    protected Configuration getMetadataColumnConfiguration(Configuration originalConf, String contentTypeId, String metadataPath) throws ConfigurationException
445    {
446        DefaultConfiguration conf = new DefaultConfiguration(originalConf);
447        
448        DefaultConfiguration metaConf = new DefaultConfiguration("metadata");
449        conf.addChild(metaConf);
450        metaConf.setAttribute("path", metadataPath);
451        
452        addContentTypesConfiguration(conf, contentTypeId);
453        
454        return conf;
455    }
456    
457    /**
458     * Get the configuration of a system column component.
459     * @param contentTypeId the content type ID.
460     * @param property the system property.
461     * @return the configuration to provide to the system column component.
462     * @throws ConfigurationException if an error occurs.
463     */
464    protected Configuration getSystemColumnConfiguration(String contentTypeId, String property) throws ConfigurationException
465    {
466        return getSystemColumnConfiguration(new DefaultConfiguration("column"), contentTypeId, property);
467    }
468    
469    /**
470     * Get the configuration of a system column component.
471     * @param originalConf the original column configuration.
472     * @param contentTypeId the content type ID.
473     * @param property the system property.
474     * @return the configuration to provide to the system column component.
475     * @throws ConfigurationException if an error occurs.
476     */
477    protected Configuration getSystemColumnConfiguration(Configuration originalConf, String contentTypeId, String property) throws ConfigurationException
478    {
479        DefaultConfiguration conf = originalConf != null ? new DefaultConfiguration(originalConf) : new DefaultConfiguration("column");
480        
481        DefaultConfiguration propConf = new DefaultConfiguration("systemProperty");
482        propConf.setAttribute("name", property);
483        conf.addChild(propConf);
484        
485        addContentTypesConfiguration(conf, contentTypeId);
486        
487        return conf;
488    }
489    
490    /**
491     * Add the content types configuration.
492     * @param conf The configuration to write to.
493     * @param contentTypeId The "base" content type ID, may be null.
494     */
495    protected void addContentTypesConfiguration(DefaultConfiguration conf, String contentTypeId)
496    {
497        Set<String> contentTypes = _searchModelHelper.getAllContentTypes(this, Collections.emptyMap());
498        DefaultConfiguration cTypesConf = new DefaultConfiguration("contentTypes");
499        conf.addChild(cTypesConf);
500        for (String contentType : contentTypes)
501        {
502            DefaultConfiguration cTypeConf = new DefaultConfiguration("type");
503            cTypeConf.setAttribute("id", contentType);
504            cTypesConf.addChild(cTypeConf);
505        }
506        
507        if (contentTypeId != null)
508        {
509            cTypesConf.setAttribute("baseId", contentTypeId);
510        }
511    }
512    
513}