001/*
002 *  Copyright 2023 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.impl;
017
018import java.util.LinkedHashMap;
019import java.util.List;
020import java.util.Map;
021
022import org.apache.commons.collections.MapUtils;
023
024import org.ametys.cms.search.model.DefaultSearchModel;
025import org.ametys.cms.search.model.LanguageAwareCriterionDefinition;
026import org.ametys.cms.search.model.SearchModelCriterionDefinition;
027import org.ametys.cms.search.model.SearchModelCriterionDefinitionHelper;
028import org.ametys.cms.search.ui.model.SearchModelCriterionViewItem;
029import org.ametys.cms.search.ui.model.SearchUIModel;
030import org.ametys.runtime.model.DefinitionContext;
031import org.ametys.runtime.model.ElementDefinition;
032import org.ametys.runtime.model.ModelViewItem;
033import org.ametys.runtime.model.View;
034import org.ametys.runtime.model.ViewHelper;
035import org.ametys.runtime.model.ViewItem;
036import org.ametys.runtime.model.ViewItemAccessor;
037import org.ametys.runtime.model.ViewItemContainer;
038
039/**
040 * Default implementation of a {@link SearchUIModel}.
041 */
042public class DefaultSearchUIModel extends DefaultSearchModel implements SearchUIModel
043{
044    /** The default plugin name for URLs */
045    protected static final String __DEFAULT_URL_PLUGIN = "cms";
046    /** The default URL for search */
047    protected static final String __DEFAULT_SEARCH_URL = "search/list.json";
048    /** The default URL for CSV export */
049    protected static final String __DEFAULT_EXPORT_CSV_URL = "search/export.csv";
050    /** The default URL for DOC export */
051    protected static final String __DEFAULT_EXPORT_DOC_URL = "search/export.doc";
052    /** The default URL for XML export */
053    protected static final String __DEFAULT_EXPORT_XML_URL = "search/export.xml";
054    /** The default URL for PDF export */
055    protected static final String __DEFAULT_EXPORT_PDF_URL = null;
056    /** The URL for print results */
057    protected static final String __DEFAULT_PRINT_URL = "search/print.html";
058    
059    /** The advanced criteria */
060    private ViewItemContainer _advancedCriteria = new View();
061    
062    /** The page size **/
063    private int _pageSize = -1;
064    
065    /** The URL for search */
066    private String _searchUrl = __DEFAULT_SEARCH_URL;
067    /** The plugin name for search */
068    private String _searchUrlPlugin = __DEFAULT_URL_PLUGIN;
069    
070    /** The URL for CSV export of results */
071    private String _exportCSVUrl = __DEFAULT_EXPORT_CSV_URL;
072    /** The plugin name for CSV export of results */
073    private String _exportCSVUrlPlugin = __DEFAULT_URL_PLUGIN;
074    /** The URL for doc export of results */
075    private String _exportDOCUrl = __DEFAULT_EXPORT_DOC_URL;
076    /** The plugin name for doc export of results */
077    private String _exportDOCUrlPlugin = __DEFAULT_URL_PLUGIN;
078    /** The URL for XML export of results */
079    private String _exportXMLUrl = __DEFAULT_EXPORT_XML_URL;
080    /** The plugin name for XML export of results */
081    private String _exportXMLUrlPlugin = __DEFAULT_URL_PLUGIN;
082    /** The URL for PDF export of results */
083    private String _exportPDFUrl = __DEFAULT_EXPORT_PDF_URL;
084    /** The plugin name for PDF export of results */
085    private String _exportPDFUrlPlugin = __DEFAULT_URL_PLUGIN;
086    
087    /** The URL for print results */    
088    private String _printUrl = __DEFAULT_PRINT_URL;
089    /** The plugin name for print results */ 
090    private String _printUrlPlugin = __DEFAULT_URL_PLUGIN;
091    
092    /** The name of the view to use for summary of the content */
093    private String _summaryView;
094    
095    /** Determine is sort is allowed on multiple join */
096    private boolean _sortOnMultipleJoin;
097    
098    /**
099     * Default constructor
100     */
101    public DefaultSearchUIModel()
102    {
103        super();
104    }
105    
106    /**
107     * Constructor by copying an existing {@link SearchUIModel}.
108     * @param searchUIModelToCopy The {@link SearchUIModel} to copy
109     * @param contextualParameters The contextual parameters
110     */
111    public DefaultSearchUIModel(SearchUIModel searchUIModelToCopy, Map<String, Object> contextualParameters)
112    {
113        super(searchUIModelToCopy, contextualParameters);
114        
115        View advancedCriteria = new View();
116        ViewItemContainer advancedCriteriaToCopy = searchUIModelToCopy.getAdvancedCriteria(contextualParameters);
117        List<ViewItem> advancedCriteriaViewItems = ViewHelper.copyViewItems(advancedCriteriaToCopy.getViewItems());
118        advancedCriteria.addViewItems(advancedCriteriaViewItems);
119        setAdvancedCriteria(advancedCriteria);
120        
121        setPageSize(searchUIModelToCopy.getPageSize(contextualParameters));
122        
123        setSearchUrl(searchUIModelToCopy.getSearchUrl(contextualParameters));
124        setSearchUrlPlugin(searchUIModelToCopy.getSearchUrlPlugin(contextualParameters));
125        
126        setExportCSVUrl(searchUIModelToCopy.getExportCSVUrl(contextualParameters));
127        setExportCSVUrlPlugin(searchUIModelToCopy.getExportCSVUrlPlugin(contextualParameters));
128        setExportDOCUrl(searchUIModelToCopy.getExportDOCUrl(contextualParameters));
129        setExportDOCUrlPlugin(searchUIModelToCopy.getExportDOCUrlPlugin(contextualParameters));
130        setExportXMLUrl(searchUIModelToCopy.getExportXMLUrl(contextualParameters));
131        setExportXMLUrlPlugin(searchUIModelToCopy.getExportXMLUrlPlugin(contextualParameters));
132        setExportPDFUrl(searchUIModelToCopy.getExportPDFUrl(contextualParameters));
133        setExportPDFUrlPlugin(searchUIModelToCopy.getExportPDFUrlPlugin(contextualParameters));
134        
135        setPrintUrl(searchUIModelToCopy.getPrintUrl(contextualParameters));
136        setPrintUrlPlugin(searchUIModelToCopy.getPrintUrlPlugin(contextualParameters));
137    }
138
139    public ViewItemContainer getAdvancedCriteria(Map<String, Object> contextualParameters)
140    {
141        return _advancedCriteria;
142    }
143    
144    /**
145     * Add criteria to the advanced criteria. The created criteria reference the given paths 
146     * @param criterionDefinitionHelper the criterion definition helper
147     * @param contextualParameters the contextual parameters
148     * @param references the paths to the items the criteria will reference 
149     */
150    public void addAdvancedCriteria(SearchModelCriterionDefinitionHelper criterionDefinitionHelper, Map<String, Object> contextualParameters, String... references)
151    {
152        for (String reference : references)
153        {
154            SearchModelCriterionDefinition criterion = criterionDefinitionHelper.createReferencingCriterionDefinition(this, reference, getContentTypes(contextualParameters));
155            addAdvancedCriterion(criterion, contextualParameters);
156        }
157    }
158    
159    /**
160     * Set the advanced criteria
161     * @param criteria The advanced criteria to set
162     */
163    public void setAdvancedCriteria(ViewItemContainer criteria)
164    {
165        _advancedCriteria = criteria;
166    }
167    
168    public int getPageSize(Map<String, Object> contextualParameters)
169    {
170        return _pageSize;
171    }
172    
173    /**
174     * Set the page size
175     * @param pageSize The page size to set. Set to negative or 0 for unlimited or default
176     */
177    public void setPageSize(int pageSize)
178    {
179        _pageSize = pageSize;
180    }
181
182    public String getSearchUrl(Map<String, Object> contextualParameters)
183    {
184        return _searchUrl;
185    }
186    
187    /**
188     * Set the URL for search
189     * @param searchUrl The URL to set
190     */
191    public void setSearchUrl(String searchUrl)
192    {
193        _searchUrl = searchUrl;
194    }
195
196    public String getSearchUrlPlugin(Map<String, Object> contextualParameters)
197    {
198        return _searchUrlPlugin;
199    }
200    
201    /**
202     * Set the plugin name for search
203     * @param searchUrlPlugin the plugin name to set
204     */
205    public void setSearchUrlPlugin(String searchUrlPlugin)
206    {
207        _searchUrlPlugin = searchUrlPlugin;
208    }
209
210    public String getExportCSVUrl(Map<String, Object> contextualParameters)
211    {
212        return _exportCSVUrl;
213    }
214    
215    /**
216     * Set the URL for CSV export of results
217     * @param exportCSVUrl The URL to set
218     */
219    public void setExportCSVUrl(String exportCSVUrl)
220    {
221        _exportCSVUrl = exportCSVUrl;
222    }
223
224    public String getExportCSVUrlPlugin(Map<String, Object> contextualParameters)
225    {
226        return _exportCSVUrlPlugin;
227    }
228    
229    /**
230     * Set the plugin name for CSV export of results
231     * @param exportCSVUrlPlugin the plugin name to set
232     */
233    public void setExportCSVUrlPlugin(String exportCSVUrlPlugin)
234    {
235        _exportCSVUrlPlugin = exportCSVUrlPlugin;
236    }
237
238    public String getExportDOCUrl(Map<String, Object> contextualParameters)
239    {
240        return _exportDOCUrl;
241    }
242
243    /**
244     * Set the URL for Doc export of results
245     * @param exportDOCUrl The URL to set
246     */
247    public void setExportDOCUrl(String exportDOCUrl)
248    {
249        _exportDOCUrl = exportDOCUrl;
250    }
251
252    public String getExportDOCUrlPlugin(Map<String, Object> contextualParameters)
253    {
254        return _exportDOCUrlPlugin;
255    }
256    
257    /**
258     * Set the plugin name for Doc export of results
259     * @param exportDOCUrlPlugin the plugin name to set
260     */
261    public void setExportDOCUrlPlugin(String exportDOCUrlPlugin)
262    {
263        _exportDOCUrlPlugin = exportDOCUrlPlugin;
264    }
265
266    public String getExportXMLUrl(Map<String, Object> contextualParameters)
267    {
268        return _exportXMLUrl;
269    }
270    
271    /**
272     * Set the URL for XML export of results
273     * @param exportXMLUrl The URL to set
274     */
275    public void setExportXMLUrl(String exportXMLUrl)
276    {
277        _exportXMLUrl = exportXMLUrl;
278    }
279
280    public String getExportXMLUrlPlugin(Map<String, Object> contextualParameters)
281    {
282        return _exportXMLUrlPlugin;
283    }
284    
285    /**
286     * Set the plugin name for XML export of results
287     * @param exportXMLUrlPlugin the plugin name to set
288     */
289    public void setExportXMLUrlPlugin(String exportXMLUrlPlugin)
290    {
291        _exportXMLUrlPlugin = exportXMLUrlPlugin;
292    }
293
294    public String getExportPDFUrl(Map<String, Object> contextualParameters)
295    {
296        return _exportPDFUrl;
297    }
298    
299    /**
300     * Set the URL for PDF export of results
301     * @param exportPDFUrl The URL to set
302     */
303    public void setExportPDFUrl(String exportPDFUrl)
304    {
305        _exportPDFUrl = exportPDFUrl;
306    }
307
308    public String getExportPDFUrlPlugin(Map<String, Object> contextualParameters)
309    {
310        return _exportPDFUrlPlugin;
311    }
312    
313    /**
314     * Set the plugin name for PDF export of results
315     * @param exportPDFUrlPlugin the plugin name to set
316     */
317    public void setExportPDFUrlPlugin(String exportPDFUrlPlugin)
318    {
319        _exportPDFUrlPlugin = exportPDFUrlPlugin;
320    }
321
322    public String getPrintUrl(Map<String, Object> contextualParameters)
323    {
324        return _printUrl;
325    }
326    
327    /**
328     * Set the URL for print results
329     * @param printUrl The URL to set
330     */
331    public void setPrintUrl(String printUrl)
332    {
333        _printUrl = printUrl;
334    }
335
336    public String getPrintUrlPlugin(Map<String, Object> contextualParameters)
337    {
338        return _printUrlPlugin;
339    }
340    
341    /**
342     * Set the plugin name for print results
343     * @param printUrlPlugin the plugin name to set
344     */
345    public void setPrintUrlPlugin(String printUrlPlugin)
346    {
347        _printUrlPlugin = printUrlPlugin;
348    }
349
350    public String getSummaryView()
351    {
352        return _summaryView;
353    }
354
355    /**
356     * Set The name of the view to use for summary of the content
357     * @param summaryView the name to set
358     */
359    public void setSummaryView(String summaryView)
360    {
361        _summaryView = summaryView;
362    }
363    
364    /**
365     * Indicates if sorting on join columns which contain at least one {@link ElementDefinition#isMultiple() multiple} {@link ElementDefinition definition} in its path (intermediate ones only) must be allowed.
366     * <br>If the final definition of the path is multiple, the column will not be sortable though.
367     * @return <code>true</code> if sorting on join columns which contains at least one multiple item in its path (intermediate ones only) must be allowed.
368     */
369    protected boolean allowSortOnMultipleJoin()
370    {
371        return _sortOnMultipleJoin;
372    }
373    
374    /**
375     * Set to <code>true</code> to allow sort on multiple join (default to <code>false</code>)
376     * @param sortOnMultipleJoin <code>true</code> to allow sort on multiple join, <code>false</code> otherwise
377     */
378    public void setAllowSortOnMultipleJoin(boolean sortOnMultipleJoin)
379    {
380        _sortOnMultipleJoin = sortOnMultipleJoin;
381    }
382    
383    @Override
384    public Map<String, Object> toJSON(Map<String, Object> contextualParameters)
385    {
386        Map<String, Object> jsonObject = super.toJSON(contextualParameters);
387        
388        jsonObject.put("pageSize", getPageSize(contextualParameters));
389        jsonObject.put("workspace", getWorkspace(contextualParameters));
390        jsonObject.put("searchUrl", getSearchUrl(contextualParameters));
391        jsonObject.put("searchUrlPlugin", getSearchUrlPlugin(contextualParameters));
392        jsonObject.put("exportCSVUrl", getExportCSVUrl(contextualParameters));
393        jsonObject.put("exportCSVUrlPlugin", getExportCSVUrlPlugin(contextualParameters));
394        jsonObject.put("exportDOCUrl", getExportDOCUrl(contextualParameters));
395        jsonObject.put("exportDOCUrlPlugin", getExportDOCUrlPlugin(contextualParameters));
396        jsonObject.put("exportXMLUrl", getExportXMLUrl(contextualParameters));
397        jsonObject.put("exportXMLUrlPlugin", getExportXMLUrlPlugin(contextualParameters));
398        jsonObject.put("exportPDFUrl", getExportPDFUrl(contextualParameters));
399        jsonObject.put("exportPDFUrlPlugin", getExportPDFUrlPlugin(contextualParameters));
400        jsonObject.put("printUrl", getPrintUrl(contextualParameters));
401        jsonObject.put("printUrlPlugin", getPrintUrlPlugin(contextualParameters));
402        jsonObject.put("summaryView", getSummaryView());
403        
404        jsonObject.put("advanced-criteria", _getAdvancedCriteriaListInfo(getAdvancedCriteria(contextualParameters)));
405        
406        return jsonObject;
407    }
408    
409    /**
410     * Return information on a list of advanced {@link SearchModelCriterionViewItem}, serialized as a Map.
411     * @param criteria A map of advanced criteria.
412     * @return the detailed information serialized in a Map.
413     */
414    protected Map<String, Object> _getAdvancedCriteriaListInfo(ViewItemAccessor criteria)
415    {
416        Map<String, Object> jsonObject = new LinkedHashMap<>();
417        
418        // Make a copy of the criteria view
419        ViewItemAccessor criteriaCopy = new View();
420        criteriaCopy.addViewItems(ViewHelper.copyViewItems(criteria.getViewItems()));
421
422        // Extract language criterion from the others
423        ModelViewItem languageCriterion = _getContentLanguageCriterionViewItem(criteriaCopy);
424        if (languageCriterion != null)
425        {
426            jsonObject.put("language", languageCriterion.toJSON(DefinitionContext.newInstance()));
427            
428            // Remove language criterion that is already processed
429            ViewItemAccessor languageParent = languageCriterion.getParent();
430            languageParent.removeViewItem(languageCriterion);
431        }
432        
433        Map<String, Object> criteriaObject = ViewHelper.viewItemsToJSON(criteriaCopy.getViewItems(), DefinitionContext.newInstance());
434        if (MapUtils.isNotEmpty(criteriaObject))
435        {
436            jsonObject.put("criteria", criteriaObject);
437        }
438        
439        return jsonObject;
440    }
441    
442    private ModelViewItem _getContentLanguageCriterionViewItem(ViewItemAccessor criteria)
443    {
444        for (ViewItem viewItem : criteria.getViewItems())
445        {
446            if (viewItem instanceof SearchModelCriterionViewItem criterionViewItem
447                    && criterionViewItem.getDefinition() instanceof LanguageAwareCriterionDefinition)
448            {
449                return criterionViewItem;
450            }
451            else if (viewItem instanceof ViewItemContainer itemContainer)
452            {
453                ModelViewItem contentLanguageCriterion = _getContentLanguageCriterionViewItem(itemContainer);
454                if (contentLanguageCriterion != null)
455                {
456                    return contentLanguageCriterion;
457                }
458            }
459        }
460        
461        // No criterion on contentLanguage has been found
462        return null;
463    }
464}