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