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}