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        ViewItemContainer advancedCriteriaToCopy = searchUIModelToCopy.getAdvancedCriteria(contextualParameters);
116        if (advancedCriteriaToCopy != null)
117        {
118            View advancedCriteria = new View();
119            List<ViewItem> advancedCriteriaViewItems = ViewHelper.copyViewItems(advancedCriteriaToCopy.getViewItems());
120            advancedCriteria.addViewItems(advancedCriteriaViewItems);
121            setAdvancedCriteria(advancedCriteria);
122        }
123        
124        setPageSize(searchUIModelToCopy.getPageSize(contextualParameters));
125        
126        setSearchUrl(searchUIModelToCopy.getSearchUrl(contextualParameters));
127        setSearchUrlPlugin(searchUIModelToCopy.getSearchUrlPlugin(contextualParameters));
128        
129        setExportCSVUrl(searchUIModelToCopy.getExportCSVUrl(contextualParameters));
130        setExportCSVUrlPlugin(searchUIModelToCopy.getExportCSVUrlPlugin(contextualParameters));
131        setExportDOCUrl(searchUIModelToCopy.getExportDOCUrl(contextualParameters));
132        setExportDOCUrlPlugin(searchUIModelToCopy.getExportDOCUrlPlugin(contextualParameters));
133        setExportXMLUrl(searchUIModelToCopy.getExportXMLUrl(contextualParameters));
134        setExportXMLUrlPlugin(searchUIModelToCopy.getExportXMLUrlPlugin(contextualParameters));
135        setExportPDFUrl(searchUIModelToCopy.getExportPDFUrl(contextualParameters));
136        setExportPDFUrlPlugin(searchUIModelToCopy.getExportPDFUrlPlugin(contextualParameters));
137        
138        setPrintUrl(searchUIModelToCopy.getPrintUrl(contextualParameters));
139        setPrintUrlPlugin(searchUIModelToCopy.getPrintUrlPlugin(contextualParameters));
140    }
141
142    public ViewItemContainer getAdvancedCriteria(Map<String, Object> contextualParameters)
143    {
144        return _advancedCriteria;
145    }
146    
147    /**
148     * Add criteria to the advanced criteria. The created criteria reference the given paths 
149     * @param criterionDefinitionHelper the criterion definition helper
150     * @param contextualParameters the contextual parameters
151     * @param references the paths to the items the criteria will reference 
152     */
153    public void addAdvancedCriteria(SearchModelCriterionDefinitionHelper criterionDefinitionHelper, Map<String, Object> contextualParameters, String... references)
154    {
155        for (String reference : references)
156        {
157            SearchModelCriterionDefinition criterion = criterionDefinitionHelper.createReferencingCriterionDefinition(this, reference, getContentTypes(contextualParameters));
158            addAdvancedCriterion(criterion, contextualParameters);
159        }
160    }
161    
162    /**
163     * Set the advanced criteria
164     * @param criteria The advanced criteria to set
165     */
166    public void setAdvancedCriteria(ViewItemContainer criteria)
167    {
168        _advancedCriteria = criteria;
169    }
170    
171    public int getPageSize(Map<String, Object> contextualParameters)
172    {
173        return _pageSize;
174    }
175    
176    /**
177     * Set the page size
178     * @param pageSize The page size to set. Set to negative or 0 for unlimited or default
179     */
180    public void setPageSize(int pageSize)
181    {
182        _pageSize = pageSize;
183    }
184
185    public String getSearchUrl(Map<String, Object> contextualParameters)
186    {
187        return _searchUrl;
188    }
189    
190    /**
191     * Set the URL for search
192     * @param searchUrl The URL to set
193     */
194    public void setSearchUrl(String searchUrl)
195    {
196        _searchUrl = searchUrl;
197    }
198
199    public String getSearchUrlPlugin(Map<String, Object> contextualParameters)
200    {
201        return _searchUrlPlugin;
202    }
203    
204    /**
205     * Set the plugin name for search
206     * @param searchUrlPlugin the plugin name to set
207     */
208    public void setSearchUrlPlugin(String searchUrlPlugin)
209    {
210        _searchUrlPlugin = searchUrlPlugin;
211    }
212
213    public String getExportCSVUrl(Map<String, Object> contextualParameters)
214    {
215        return _exportCSVUrl;
216    }
217    
218    /**
219     * Set the URL for CSV export of results
220     * @param exportCSVUrl The URL to set
221     */
222    public void setExportCSVUrl(String exportCSVUrl)
223    {
224        _exportCSVUrl = exportCSVUrl;
225    }
226
227    public String getExportCSVUrlPlugin(Map<String, Object> contextualParameters)
228    {
229        return _exportCSVUrlPlugin;
230    }
231    
232    /**
233     * Set the plugin name for CSV export of results
234     * @param exportCSVUrlPlugin the plugin name to set
235     */
236    public void setExportCSVUrlPlugin(String exportCSVUrlPlugin)
237    {
238        _exportCSVUrlPlugin = exportCSVUrlPlugin;
239    }
240
241    public String getExportDOCUrl(Map<String, Object> contextualParameters)
242    {
243        return _exportDOCUrl;
244    }
245
246    /**
247     * Set the URL for Doc export of results
248     * @param exportDOCUrl The URL to set
249     */
250    public void setExportDOCUrl(String exportDOCUrl)
251    {
252        _exportDOCUrl = exportDOCUrl;
253    }
254
255    public String getExportDOCUrlPlugin(Map<String, Object> contextualParameters)
256    {
257        return _exportDOCUrlPlugin;
258    }
259    
260    /**
261     * Set the plugin name for Doc export of results
262     * @param exportDOCUrlPlugin the plugin name to set
263     */
264    public void setExportDOCUrlPlugin(String exportDOCUrlPlugin)
265    {
266        _exportDOCUrlPlugin = exportDOCUrlPlugin;
267    }
268
269    public String getExportXMLUrl(Map<String, Object> contextualParameters)
270    {
271        return _exportXMLUrl;
272    }
273    
274    /**
275     * Set the URL for XML export of results
276     * @param exportXMLUrl The URL to set
277     */
278    public void setExportXMLUrl(String exportXMLUrl)
279    {
280        _exportXMLUrl = exportXMLUrl;
281    }
282
283    public String getExportXMLUrlPlugin(Map<String, Object> contextualParameters)
284    {
285        return _exportXMLUrlPlugin;
286    }
287    
288    /**
289     * Set the plugin name for XML export of results
290     * @param exportXMLUrlPlugin the plugin name to set
291     */
292    public void setExportXMLUrlPlugin(String exportXMLUrlPlugin)
293    {
294        _exportXMLUrlPlugin = exportXMLUrlPlugin;
295    }
296
297    public String getExportPDFUrl(Map<String, Object> contextualParameters)
298    {
299        return _exportPDFUrl;
300    }
301    
302    /**
303     * Set the URL for PDF export of results
304     * @param exportPDFUrl The URL to set
305     */
306    public void setExportPDFUrl(String exportPDFUrl)
307    {
308        _exportPDFUrl = exportPDFUrl;
309    }
310
311    public String getExportPDFUrlPlugin(Map<String, Object> contextualParameters)
312    {
313        return _exportPDFUrlPlugin;
314    }
315    
316    /**
317     * Set the plugin name for PDF export of results
318     * @param exportPDFUrlPlugin the plugin name to set
319     */
320    public void setExportPDFUrlPlugin(String exportPDFUrlPlugin)
321    {
322        _exportPDFUrlPlugin = exportPDFUrlPlugin;
323    }
324
325    public String getPrintUrl(Map<String, Object> contextualParameters)
326    {
327        return _printUrl;
328    }
329    
330    /**
331     * Set the URL for print results
332     * @param printUrl The URL to set
333     */
334    public void setPrintUrl(String printUrl)
335    {
336        _printUrl = printUrl;
337    }
338
339    public String getPrintUrlPlugin(Map<String, Object> contextualParameters)
340    {
341        return _printUrlPlugin;
342    }
343    
344    /**
345     * Set the plugin name for print results
346     * @param printUrlPlugin the plugin name to set
347     */
348    public void setPrintUrlPlugin(String printUrlPlugin)
349    {
350        _printUrlPlugin = printUrlPlugin;
351    }
352
353    public String getSummaryView()
354    {
355        return _summaryView;
356    }
357
358    /**
359     * Set The name of the view to use for summary of the content
360     * @param summaryView the name to set
361     */
362    public void setSummaryView(String summaryView)
363    {
364        _summaryView = summaryView;
365    }
366    
367    /**
368     * 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.
369     * <br>If the final definition of the path is multiple, the column will not be sortable though.
370     * @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.
371     */
372    protected boolean allowSortOnMultipleJoin()
373    {
374        return _sortOnMultipleJoin;
375    }
376    
377    /**
378     * Set to <code>true</code> to allow sort on multiple join (default to <code>false</code>)
379     * @param sortOnMultipleJoin <code>true</code> to allow sort on multiple join, <code>false</code> otherwise
380     */
381    public void setAllowSortOnMultipleJoin(boolean sortOnMultipleJoin)
382    {
383        _sortOnMultipleJoin = sortOnMultipleJoin;
384    }
385    
386    @Override
387    public Map<String, Object> toJSON(Map<String, Object> contextualParameters)
388    {
389        Map<String, Object> jsonObject = super.toJSON(contextualParameters);
390        
391        jsonObject.put("pageSize", getPageSize(contextualParameters));
392        jsonObject.put("workspace", getWorkspace(contextualParameters));
393        jsonObject.put("searchUrl", getSearchUrl(contextualParameters));
394        jsonObject.put("searchUrlPlugin", getSearchUrlPlugin(contextualParameters));
395        jsonObject.put("exportCSVUrl", getExportCSVUrl(contextualParameters));
396        jsonObject.put("exportCSVUrlPlugin", getExportCSVUrlPlugin(contextualParameters));
397        jsonObject.put("exportDOCUrl", getExportDOCUrl(contextualParameters));
398        jsonObject.put("exportDOCUrlPlugin", getExportDOCUrlPlugin(contextualParameters));
399        jsonObject.put("exportXMLUrl", getExportXMLUrl(contextualParameters));
400        jsonObject.put("exportXMLUrlPlugin", getExportXMLUrlPlugin(contextualParameters));
401        jsonObject.put("exportPDFUrl", getExportPDFUrl(contextualParameters));
402        jsonObject.put("exportPDFUrlPlugin", getExportPDFUrlPlugin(contextualParameters));
403        jsonObject.put("printUrl", getPrintUrl(contextualParameters));
404        jsonObject.put("printUrlPlugin", getPrintUrlPlugin(contextualParameters));
405        jsonObject.put("summaryView", getSummaryView());
406        
407        jsonObject.put("advanced-criteria", _getAdvancedCriteriaListInfo(getAdvancedCriteria(contextualParameters)));
408        
409        return jsonObject;
410    }
411    
412    /**
413     * Return information on a list of advanced {@link SearchModelCriterionViewItem}, serialized as a Map.
414     * @param criteria A map of advanced criteria.
415     * @return the detailed information serialized in a Map.
416     */
417    protected Map<String, Object> _getAdvancedCriteriaListInfo(ViewItemAccessor criteria)
418    {
419        Map<String, Object> jsonObject = new LinkedHashMap<>();
420
421        if (criteria != null)
422        {
423            // Make a copy of the criteria view
424            ViewItemAccessor criteriaCopy = new View();
425            criteriaCopy.addViewItems(ViewHelper.copyViewItems(criteria.getViewItems()));
426    
427            // Extract language criterion from the others
428            ModelViewItem languageCriterion = _getContentLanguageCriterionViewItem(criteriaCopy);
429            if (languageCriterion != null)
430            {
431                jsonObject.put("language", languageCriterion.toJSON(DefinitionContext.newInstance()));
432                
433                // Remove language criterion that is already processed
434                ViewItemAccessor languageParent = languageCriterion.getParent();
435                languageParent.removeViewItem(languageCriterion);
436            }
437            
438            Map<String, Object> criteriaObject = ViewHelper.viewItemsToJSON(criteriaCopy.getViewItems(), DefinitionContext.newInstance());
439            if (MapUtils.isNotEmpty(criteriaObject))
440            {
441                jsonObject.put("criteria", criteriaObject);
442            }
443        }
444        
445        return jsonObject;
446    }
447    
448    private ModelViewItem _getContentLanguageCriterionViewItem(ViewItemAccessor criteria)
449    {
450        for (ViewItem viewItem : criteria.getViewItems())
451        {
452            if (viewItem instanceof SearchModelCriterionViewItem criterionViewItem
453                    && criterionViewItem.getDefinition() instanceof LanguageAwareCriterionDefinition)
454            {
455                return criterionViewItem;
456            }
457            else if (viewItem instanceof ViewItemContainer itemContainer)
458            {
459                ModelViewItem contentLanguageCriterion = _getContentLanguageCriterionViewItem(itemContainer);
460                if (contentLanguageCriterion != null)
461                {
462                    return contentLanguageCriterion;
463                }
464            }
465        }
466        
467        // No criterion on contentLanguage has been found
468        return null;
469    }
470}