001/*
002 *  Copyright 2022 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.plugins.calendar.search;
017
018import java.time.ZonedDateTime;
019import java.util.Arrays;
020import java.util.List;
021import java.util.Map;
022import java.util.Map.Entry;
023
024import org.apache.avalon.framework.parameters.ParameterException;
025import org.apache.avalon.framework.parameters.Parameters;
026import org.apache.cocoon.components.ContextHelper;
027import org.apache.cocoon.xml.AttributesImpl;
028import org.apache.cocoon.xml.XMLUtils;
029import org.apache.commons.lang3.StringUtils;
030import org.xml.sax.ContentHandler;
031import org.xml.sax.SAXException;
032
033import org.ametys.cms.tag.ColorableTag;
034import org.ametys.cms.tag.Tag;
035import org.ametys.core.util.DateUtils;
036import org.ametys.core.util.URIUtils;
037import org.ametys.plugins.calendar.events.EventsFilterHelper.DateTimeRange;
038import org.ametys.web.frontoffice.search.SearchService;
039import org.ametys.web.frontoffice.search.instance.SearchServiceInstance;
040import org.ametys.web.frontoffice.search.instance.model.RightCheckingMode;
041import org.ametys.web.frontoffice.search.metamodel.AdditionalParameterValueMap;
042import org.ametys.web.frontoffice.search.requesttime.SearchComponentArguments;
043import org.ametys.web.repository.page.Page;
044import org.ametys.web.repository.page.ZoneItem;
045
046/**
047 * Calendar search service
048 *
049 */
050public class CalendarSearchService extends SearchService
051{
052    /** Id of service */
053    public static final String SERVICE_ID = "org.ametys.plugins.calendar.SearchEvents";
054    
055    /** Parameter to enable calendar search components */
056    public static final String ENABLE_CALENDAR_PARAMETER_NAME = "calendar";
057    
058    /** Service parameter for tags' categories */
059    public static final String SERVICE_PARAM_TAG_CATEGORIES = "tag-categories";
060    
061    /** Service parameter for contents' view */
062    public static final String SERVICE_PARAM_CONTENT_VIEW = "calendarContentView";
063    
064    /** Service parameter for calendar content types */
065    public static final String SERVICE_PARAM_CONTENT_TYPES = "calendarContentTypes";
066    
067    /** The parameter name for saving user preferences */
068    public static final String SERVICE_PARAM_SAVE_USER_PREFS = "saveFilterInUserPrefs";
069    
070    /** Parameter for start date */
071    public static final String PARAM_START_DATE = "start";
072    
073    /** Parameter for end date */
074    public static final String PARAM_END_DATE = "end";
075    
076    /** Parameter for selected tags */
077    public static final String PARAM_TAGS = "tags";
078    
079    /** Parameter for selected tags */
080    public static final String PARAM_SUBMIT = "submit-form";
081    
082    /** Attribute name for content's start date */
083    public static final String START_DATE_ATTRIBUTE_NAME = "start-date";
084    
085    /** Attribute name for content's end date */
086    public static final String END_DATE_ATTRIBUTE_NAME = "end-date";
087    
088    private static final String __TAGS_ALL_VALUE = "_ALL";
089    
090    private static final String __ENABLE_SAVE_USER_PREFS_PARAMETER_NAME = "saveUserPrefs";
091    
092    @Override
093    public boolean isCacheable(Page currentPage, ZoneItem zoneItem)
094    {
095        boolean isDebug = _isDebug(ContextHelper.getRequest(_context), _renderingContextHandler);
096        SearchServiceInstance serviceInstance = _searchServiceInstanceManager.get(zoneItem.getId());
097        
098        if (!isDebug)
099        {
100            // Cacheable if no user input and right checking mode is on 'FAST'
101            return !_hasUserInput(serviceInstance) && serviceInstance.getRightCheckingMode() == RightCheckingMode.FAST && !hasTagCategories(serviceInstance);
102        }
103        
104        return false;
105    }
106    
107    /**
108     * Determines if tags' categories filter is active
109     * @param serviceInstance the service instance
110     * @return true if tags' categories filter is active
111     */
112    public static boolean hasTagCategories(SearchServiceInstance serviceInstance)
113    {
114        List<String> categoryNames = getTagCategories(serviceInstance);
115        return categoryNames != null && !categoryNames.isEmpty();
116    }
117    
118    /**
119     * Get the tags' category
120     * @param serviceInstance the service instance
121     * @return the tags' category. Can be null if no tags' categories are configured
122     */
123    public static List<String> getTagCategories(SearchServiceInstance serviceInstance)
124    {
125        AdditionalParameterValueMap additionalParameterValues = serviceInstance.getAdditionalParameterValues();
126        return additionalParameterValues.getValue(SERVICE_PARAM_TAG_CATEGORIES);
127    }
128    
129    /**
130     * Returns <code>true</code> if save user preferences is enabled
131     * @param args the search arguments
132     * @return <code>true</code> if save user preferences is enabled
133     */
134    public static boolean saveUserPrefsEnabled(SearchComponentArguments args)
135    {
136        return args.generatorParameters().getParameterAsBoolean(__ENABLE_SAVE_USER_PREFS_PARAMETER_NAME, true);
137    }
138    
139    /**
140     * Returns <code>true</code> if selected tags filter has to be saved in user preferences
141     * @param serviceInstance the service instance
142     * @return <code>true</code> if selected tags filtes has to be saved in user preferences
143     */
144    public static boolean saveFilterInUserPrefs(SearchServiceInstance serviceInstance)
145    {
146        AdditionalParameterValueMap additionalParameterValues = serviceInstance.getAdditionalParameterValues();
147        return additionalParameterValues.getValue(SERVICE_PARAM_SAVE_USER_PREFS);
148    }
149    
150    /**
151     * Get the active tags
152     * @param args the search arguments
153     * @return the active tags
154     */
155    public static List<String> getSelectedTags(SearchComponentArguments args)
156    {
157        String parameter = args.generatorParameters().getParameter(PARAM_TAGS, "");
158        if (__TAGS_ALL_VALUE.equals(parameter))
159        {
160            return List.of();
161        }
162        String[] selectedTags = StringUtils.isNotEmpty(parameter) ? parameter.split(",") : new String[0];
163        return Arrays.asList(selectedTags);
164    }
165    
166    /**
167     * Determines if is calendar service
168     * @param args the search component arguments
169     * @return true if it is calendar service
170     */
171    public static boolean isActive(SearchComponentArguments args)
172    {
173        return args.generatorParameters().getParameterAsBoolean(ENABLE_CALENDAR_PARAMETER_NAME, false);
174    }
175    
176    /**
177     * Determines if form is submit to get results
178     * @param args the search component arguments
179     * @return true if form is submit
180     */
181    public static boolean isFormSubmit(SearchComponentArguments args)
182    {
183        return args.generatorParameters().getParameterAsBoolean(PARAM_SUBMIT, false);
184    }
185    
186    /**
187     * Get search date range
188     * @param args the search component arguments
189     * @return the date time range
190     */
191    public static DateTimeRange getDateRange(SearchComponentArguments args)
192    {
193        Parameters parameters = args.generatorParameters();
194        if (parameters.isParameter(PARAM_START_DATE) && parameters.isParameter(PARAM_END_DATE))
195        {
196            try
197            {
198                String start = URIUtils.decode(parameters.getParameter(CalendarSearchService.PARAM_START_DATE));
199                String end = URIUtils.decode(parameters.getParameter(CalendarSearchService.PARAM_END_DATE));
200                
201                ZonedDateTime fromDateTime = DateUtils.parseZonedDateTime(start);
202                ZonedDateTime untilDateTime = DateUtils.parseZonedDateTime(end);
203                
204                return new DateTimeRange(fromDateTime, untilDateTime);
205            }
206            catch (ParameterException e)
207            {
208                // ignore
209            }
210        }
211        
212        return null;
213    }
214    
215    /**
216     * SAX a tag
217     * @param handler the content handler
218     * @param tag the tag
219     * @throws SAXException if an error occurred
220     */
221    public static void saxTag(ContentHandler handler, Tag tag) throws SAXException
222    {
223        saxTag(handler, new AttributesImpl(), tag);
224    }
225    
226    /**
227     * SAX a tag
228     * @param handler the content handler
229     * @param attrs The attributes 
230     * @param tag the tag
231     * @throws SAXException if an error occurred
232     */
233    public static void saxTag(ContentHandler handler, AttributesImpl attrs, Tag tag) throws SAXException
234    {
235        if (tag != null)
236        {
237            attrs.addCDATAAttribute("name", tag.getName());
238            XMLUtils.startElement(handler, "tag", attrs);
239            
240            XMLUtils.startElement(handler, "label");
241            tag.getTitle().toSAX(handler);
242            XMLUtils.endElement(handler, "label");
243            
244            saxTagColor(handler, tag);
245            
246            XMLUtils.endElement(handler, "tag");
247        }
248    }
249    
250    /**
251     * SAX the color of a tag
252     * @param handler the content handler
253     * @param tag the tag
254     * @throws SAXException if an error occurred
255     */
256    public static void saxTagColor(ContentHandler handler, Tag tag) throws SAXException
257    {
258        if (tag instanceof ColorableTag)
259        {
260            String colorIndex = ((ColorableTag) tag).getColor(true);
261            Map<String, String> colors = ((ColorableTag) tag).getColorComponent().getColors().get(colorIndex);
262            if (colors != null)
263            {
264                XMLUtils.startElement(handler, "color");
265                for (Entry<String, String> entry : colors.entrySet())
266                {
267                    XMLUtils.createElement(handler, entry.getKey(), entry.getValue());
268                }
269                XMLUtils.endElement(handler, "color");
270            }
271        }
272    }
273}