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 getExportDOCUrl(Map<String, Object> contextualParameters)
246    {
247        return "search/export.doc";
248    }
249
250    @Override
251    public String getExportDOCUrlPlugin(Map<String, Object> contextualParameters)
252    {
253        return "cms";
254    }
255
256    @Override
257    public String getExportXMLUrl(Map<String, Object> contextualParameters)
258    {
259        return "search/export.xml";
260    }
261
262    @Override
263    public String getExportXMLUrlPlugin(Map<String, Object> contextualParameters)
264    {
265        return "cms";
266    }
267
268    @Override
269    public String getPrintUrl(Map<String, Object> contextualParameters)
270    {
271        return "search/print.html";
272    }
273
274    @Override
275    public String getPrintUrlPlugin(Map<String, Object> contextualParameters)
276    {
277        return "cms";
278    }
279    
280    @Override
281    public String getSummaryView()
282    {
283        return null;
284    }
285    
286    /**
287     * Get the configuration of a metadata criteria component.
288     * @param contentTypeId the content type ID.
289     * @param path the field path.
290     * @param operator the criteria operator, can be null.
291     * @return the configuration to provide to the metadata criteria component.
292     * @throws ConfigurationException if an error occurs.
293     */
294    protected Configuration getIndexingFieldCriteriaConfiguration(String contentTypeId, String path, Operator operator) throws ConfigurationException
295    {
296        return getIndexingFieldCriteriaConfiguration(contentTypeId, path, operator, null);
297    }
298    
299    /**
300     * Get the configuration of a metadata criteria component.
301     * @param contentTypeId the content type ID.
302     * @param path the field path.
303     * @param operator the criteria operator, can be null.
304     * @param group The group. Can ne null.
305     * @return the configuration to provide to the metadata criteria component.
306     * @throws ConfigurationException if an error occurs.
307     */
308    protected Configuration getIndexingFieldCriteriaConfiguration(String contentTypeId, String path, Operator operator, I18nizableText group) throws ConfigurationException
309    {
310        return getIndexingFieldCriteriaConfiguration(new DefaultConfiguration("criteria"), contentTypeId, path, operator, group);
311    }
312    
313    /**
314     * Get the configuration of a metadata criteria component.
315     * @param originalConf the original criteria configuration.
316     * @param contentTypeId the content type ID.
317     * @param path the field path, separated by '/'.
318     * @param operator the criteria operator, can be null.
319     * @param group The group. Can ne null.
320     * @return the configuration to provide to the metadata criterion component.
321     * @throws ConfigurationException if an error occurs.
322     */
323    protected Configuration getIndexingFieldCriteriaConfiguration(Configuration originalConf, String contentTypeId, String path, Operator operator, I18nizableText group) throws ConfigurationException
324    {
325        DefaultConfiguration conf = new DefaultConfiguration(originalConf);
326        
327        DefaultConfiguration metaConf = new DefaultConfiguration("field");
328        conf.addChild(metaConf);
329        metaConf.setAttribute("path", path);
330        
331        if (operator != null)
332        {
333            DefaultConfiguration opConf = new DefaultConfiguration("test-operator");
334            conf.addChild(opConf);
335            opConf.setValue(operator.getName());
336        }
337        
338        addContentTypesConfiguration(conf, contentTypeId);
339        
340        if (group != null)
341        {
342            DefaultConfiguration groupConf = new DefaultConfiguration("group");
343            groupConf.setAttribute("i18n", group.isI18n());
344            groupConf.setValue(group.isI18n() ? group.getCatalogue() + ":" + group.getKey() : group.getLabel());
345            conf.addChild(groupConf);
346        }
347        
348        return conf;
349    }
350    
351    /**
352     * Get the configuration of a system criteria component.
353     * @param contentTypeId the content type ID.
354     * @param property the system property.
355     * @return the configuration to provide to the system criterion component.
356     */
357    protected Configuration getSystemCriteriaConfiguration(String contentTypeId, String property)
358    {
359        return getSystemCriteriaConfiguration(contentTypeId, property, null);
360    }
361    
362    /**
363     * Get the configuration of a system criteria component.
364     * @param contentTypeId the content type ID.
365     * @param property the system property.
366     * @param group The group. Can ne null.
367     * @return the configuration to provide to the system criterion component.
368     */
369    protected Configuration getSystemCriteriaConfiguration(String contentTypeId, String property, I18nizableText group)
370    {
371        try
372        {
373            return getSystemCriteriaConfiguration(new DefaultConfiguration("criteria"), contentTypeId, property, group);
374        }
375        catch (ConfigurationException e)
376        {
377            // Ignore, just can't happen.
378            return null;
379        }
380    }
381    
382    /**
383     * Get the configuration of a system criteria component.
384     * @param originalConf the original column configuration.
385     * @param contentTypeId the content type ID.
386     * @param property the system property.
387     * @param group The group. Can ne null.
388     * @return the configuration to provide to the system criterion component.
389     * @throws ConfigurationException if an error occurs.
390     */
391    protected Configuration getSystemCriteriaConfiguration(Configuration originalConf, String contentTypeId, String property, I18nizableText group) throws ConfigurationException
392    {
393        DefaultConfiguration conf = new DefaultConfiguration(originalConf);
394        
395        DefaultConfiguration propConf = new DefaultConfiguration("systemProperty");
396        propConf.setAttribute("name", property);
397        conf.addChild(propConf);
398        
399        addContentTypesConfiguration(conf, contentTypeId);
400        
401        if (group != null)
402        {
403            DefaultConfiguration groupConf = new DefaultConfiguration("group");
404            groupConf.setAttribute("i18n", group.isI18n());
405            groupConf.setValue(group.isI18n() ? group.getCatalogue() + ":" + group.getKey() : group.getLabel());
406            conf.addChild(groupConf);
407        }
408        
409        return conf;
410    }
411    
412    /**
413     * Get the configuration of a custom criteria component.
414     * @param originalConf the original column configuration.
415     * @param contentTypeId the content type ID.
416     * @param customCriterionId the custom criterion id
417     * @param group The group. Can be null.
418     * @return the configuration to provide to the custom criterion component.
419     * @throws ConfigurationException if an error occurs.
420     */
421    protected Configuration getCustomCriteriaConfiguration(Configuration originalConf, String contentTypeId, String customCriterionId, I18nizableText group) throws ConfigurationException
422    {
423        DefaultConfiguration conf = new DefaultConfiguration(originalConf);
424        
425        DefaultConfiguration customCriterionConf = new DefaultConfiguration("customCriterion");
426        customCriterionConf.setAttribute("id", customCriterionId);
427        conf.addChild(customCriterionConf);
428        
429        addContentTypesConfiguration(conf, contentTypeId);
430        
431        if (group != null)
432        {
433            DefaultConfiguration groupConf = new DefaultConfiguration("group");
434            groupConf.setAttribute("i18n", group.isI18n());
435            groupConf.setValue(group.isI18n() ? group.getCatalogue() + ":" + group.getKey() : group.getLabel());
436            conf.addChild(groupConf);
437        }
438        
439        return conf;
440    }
441
442    /**
443     * Get the configuration of a metadata column component.
444     * @param contentTypeId the content type ID.
445     * @param metadataPath the metadata path.
446     * @return the configuration to provide to the metadata column component.
447     * @throws ConfigurationException if an error occurs.
448     */
449    protected Configuration getMetadataColumnConfiguration(String contentTypeId, String metadataPath) throws ConfigurationException
450    {
451        return getMetadataColumnConfiguration(new DefaultConfiguration("column"), contentTypeId, metadataPath);
452    }
453    
454    /**
455     * Get the configuration of a metadata column component.
456     * @param originalConf the original column configuration.
457     * @param contentTypeId the content type ID, may be null.
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(Configuration originalConf, String contentTypeId, String metadataPath) throws ConfigurationException
463    {
464        DefaultConfiguration conf = new DefaultConfiguration(originalConf);
465        
466        DefaultConfiguration metaConf = new DefaultConfiguration("metadata");
467        conf.addChild(metaConf);
468        metaConf.setAttribute("path", metadataPath);
469        
470        addContentTypesConfiguration(conf, contentTypeId);
471        
472        // conf for allowing sort on multiple join
473        DefaultConfiguration allowSortOnMultipleJoinConf = new DefaultConfiguration("allow-sort-on-multiple-join");
474        allowSortOnMultipleJoinConf.setValue(allowSortOnMultipleJoin());
475        conf.addChild(allowSortOnMultipleJoinConf);
476        
477        return conf;
478    }
479    
480    /**
481     * Get the configuration of a system column component.
482     * @param contentTypeId the content type ID.
483     * @param property the system property.
484     * @return the configuration to provide to the system column component.
485     * @throws ConfigurationException if an error occurs.
486     */
487    protected Configuration getSystemColumnConfiguration(String contentTypeId, String property) throws ConfigurationException
488    {
489        return getSystemColumnConfiguration(new DefaultConfiguration("column"), contentTypeId, property);
490    }
491    
492    /**
493     * Get the configuration of a system column component.
494     * @param originalConf the original column configuration.
495     * @param contentTypeId the content type ID.
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(Configuration originalConf, String contentTypeId, String property) throws ConfigurationException
501    {
502        DefaultConfiguration conf = originalConf != null ? new DefaultConfiguration(originalConf) : new DefaultConfiguration("column");
503        
504        DefaultConfiguration propConf = new DefaultConfiguration("systemProperty");
505        propConf.setAttribute("name", property);
506        conf.addChild(propConf);
507        
508        addContentTypesConfiguration(conf, contentTypeId);
509        
510        return conf;
511    }
512    
513    /**
514     * Add the content types configuration.
515     * @param conf The configuration to write to.
516     * @param contentTypeId The "base" content type ID, may be null.
517     */
518    protected void addContentTypesConfiguration(DefaultConfiguration conf, String contentTypeId)
519    {
520        Set<String> contentTypes = _searchModelHelper.getAllContentTypes(this, Collections.emptyMap());
521        DefaultConfiguration cTypesConf = new DefaultConfiguration("contentTypes");
522        conf.addChild(cTypesConf);
523        for (String contentType : contentTypes)
524        {
525            DefaultConfiguration cTypeConf = new DefaultConfiguration("type");
526            cTypeConf.setAttribute("id", contentType);
527            cTypesConf.addChild(cTypeConf);
528        }
529        
530        if (contentTypeId != null)
531        {
532            cTypesConf.setAttribute("baseId", contentTypeId);
533        }
534    }
535    
536}