001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.jsp.search.config.parser.simplesearch; 029 030import org.opencms.file.CmsFile; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsResource; 033import org.opencms.i18n.CmsLocaleManager; 034import org.opencms.jsp.search.config.parser.simplesearch.CmsConfigurationBean.CombinationMode; 035import org.opencms.jsp.search.config.parser.simplesearch.daterestrictions.CmsDateRestrictionParser; 036import org.opencms.jsp.search.config.parser.simplesearch.daterestrictions.I_CmsDateRestriction; 037import org.opencms.jsp.search.config.parser.simplesearch.preconfiguredrestrictions.CmsRestrictionsBean; 038import org.opencms.main.CmsException; 039import org.opencms.main.CmsLog; 040import org.opencms.relations.CmsCategoryService; 041import org.opencms.relations.CmsLink; 042import org.opencms.util.CmsGeoUtil; 043import org.opencms.util.CmsStringUtil; 044import org.opencms.util.CmsUUID; 045import org.opencms.xml.CmsXmlUtils; 046import org.opencms.xml.content.CmsXmlContent; 047import org.opencms.xml.content.CmsXmlContentFactory; 048import org.opencms.xml.content.CmsXmlContentValueLocation; 049import org.opencms.xml.types.CmsXmlVfsFileValue; 050import org.opencms.xml.types.I_CmsXmlContentValue; 051 052import java.util.ArrayList; 053import java.util.Arrays; 054import java.util.Collections; 055import java.util.HashMap; 056import java.util.LinkedHashMap; 057import java.util.List; 058import java.util.Locale; 059import java.util.Map; 060import java.util.stream.Collectors; 061 062import org.apache.commons.logging.Log; 063 064/** Utils to read and update the list configuration. */ 065public final class CmsConfigParserUtils { 066 067 /** The logger for this class. */ 068 static final Log LOG = CmsLog.getLog(CmsConfigParserUtils.class.getName()); 069 070 /** List configuration node name and field key. */ 071 public static final String N_BLACKLIST = "Blacklist"; 072 073 /** List configuration node name and field key. */ 074 public static final String N_CATEGORY = "Category"; 075 076 /** List configuration node name and field key. */ 077 private static final String N_CATEGORY_FOLDER_RESTRICTION = "CategoryFolderFilter"; 078 079 /** List configuration node name and field key. */ 080 private static final String N_COORDINATES = "Coordinates"; 081 082 /** List configuration node name and field key. */ 083 private static final String N_FOLDER = "Folder"; 084 085 /** List configuration node name for the category mode. */ 086 public static final String N_CATEGORY_MODE = "CategoryMode"; 087 088 /** XML content node name. */ 089 public static final String N_DATE_RESTRICTION = "DateRestriction"; 090 091 /** List configuration node name and field key. */ 092 public static final String N_DISPLAY_TYPE = "TypesToCollect"; 093 094 /** List configuration node name and field key. */ 095 public static final String N_FILTER_MULTI_DAY = "FilterMultiDay"; 096 097 /** List configuration node name and field key. */ 098 public static final String N_FILTER_QUERY = "FilterQuery"; 099 100 /** List configuration node name and field key. */ 101 public static final String N_GEO_FILTER = "GeoFilter"; 102 103 /** List configuration node name and field key. */ 104 public static final String N_KEY = "Key"; 105 106 /** List configuration node name and field key. */ 107 public static final String N_PARAMETER = "Parameter"; 108 109 /** List configuration node name and field key. */ 110 public static final String N_RADIUS = "Radius"; 111 112 /** List configuration node name and field key. */ 113 public static final String N_SEARCH_FOLDER = "SearchFolder"; 114 115 /** List configuration node name and field key. */ 116 public static final String N_SHOW_EXPIRED = "ShowExpired"; 117 118 /** List configuration node name and field key. */ 119 public static final String N_SORT_ORDER = "SortOrder"; 120 121 /** List configuration node name and field key. */ 122 public static final String N_TITLE = "Title"; 123 124 /** List configuration node name and field key. */ 125 public static final String N_VALUE = "Value"; 126 127 /** List configuration node name and field key. */ 128 public static final String N_MAX_RESULTS = "MaxResults"; 129 130 /** List configuration node name and field key. */ 131 public static final String N_PRECONFIGURED_FILTER_QUERY = "PreconfiguredFilterQuery"; 132 133 /** List configuration node name and field key. */ 134 public static final String N_RULE = "Rule"; 135 136 /** The parameter fields. */ 137 public static final String[] PARAMETER_FIELDS = new String[] { 138 N_TITLE, 139 N_CATEGORY, 140 N_FILTER_MULTI_DAY, 141 N_FILTER_QUERY, 142 N_SORT_ORDER, 143 N_SHOW_EXPIRED, 144 N_MAX_RESULTS}; 145 146 /** Statically initialized map from node names to parameter names of the list configuration bean. */ 147 private static Map<String, String> PARAMS_MAP = createParamsMap(); 148 149 /** 150 * Parses the list configuration resource.<p> 151 * 152 * @param cms the CMS context to use 153 * @param res the list configuration resource 154 * 155 * @return the configuration data bean 156 */ 157 public static CmsConfigurationBean parseListConfiguration(CmsObject cms, CmsResource res) { 158 159 CmsConfigurationBean result = new CmsConfigurationBean(); 160 try { 161 CmsFile configFile = cms.readFile(res); 162 CmsXmlContent content = CmsXmlContentFactory.unmarshal(cms, configFile); 163 Locale locale = CmsLocaleManager.MASTER_LOCALE; 164 165 if (!content.hasLocale(locale)) { 166 locale = content.getLocales().get(0); 167 } 168 for (String field : PARAMETER_FIELDS) { 169 String val = content.getStringValue(cms, field, locale); 170 if (N_CATEGORY.equals(field)) { 171 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(val)) { 172 result.setCategories(Arrays.asList(val.split(","))); 173 } else { 174 result.setCategories(Collections.<String> emptyList()); 175 } 176 } else { 177 String param = PARAMS_MAP.get(field); 178 if (param != null) { 179 result.setParameterValue(param, val); 180 } else { 181 LOG.error("Parameter value for field \"" + field + "\" is unknown and hence ignored."); 182 } 183 } 184 } 185 186 I_CmsXmlContentValue restrictValue = content.getValue(N_DATE_RESTRICTION, locale); 187 if (restrictValue != null) { 188 CmsDateRestrictionParser parser = new CmsDateRestrictionParser(cms); 189 I_CmsDateRestriction restriction = parser.parse(new CmsXmlContentValueLocation(restrictValue)); 190 if (restriction == null) { 191 LOG.warn( 192 "Improper date restriction configuration in content " 193 + content.getFile().getRootPath() 194 + ", online=" 195 + cms.getRequestContext().getCurrentProject().isOnlineProject()); 196 } 197 result.setDateRestriction(restriction); 198 } 199 200 I_CmsXmlContentValue geoFilterValue = content.getValue(N_GEO_FILTER, locale); 201 if (geoFilterValue != null) { 202 String coordinatesPath = geoFilterValue.getPath() + "/" + N_COORDINATES; 203 String radiusPath = geoFilterValue.getPath() + "/" + N_RADIUS; 204 I_CmsXmlContentValue coordinatesValue = content.getValue(coordinatesPath, locale); 205 I_CmsXmlContentValue radiusValue = content.getValue(radiusPath, locale); 206 String coordinates = CmsGeoUtil.parseCoordinates(coordinatesValue.getStringValue(cms)); 207 String radius = radiusValue.getStringValue(cms); 208 boolean radiusValid = false; 209 try { 210 Float.parseFloat(radius); 211 radiusValid = true; 212 } catch (NumberFormatException e) { 213 radiusValid = false; 214 } 215 if ((coordinates != null) && radiusValid) { 216 CmsGeoFilterBean listGeoFilterBean = new CmsGeoFilterBean(coordinates, radius); 217 result.setGeoFilter(listGeoFilterBean); 218 } else { 219 LOG.warn( 220 "Improper Geo filter in content " 221 + content.getFile().getRootPath() 222 + ", online=" 223 + cms.getRequestContext().getCurrentProject().isOnlineProject()); 224 } 225 } 226 227 I_CmsXmlContentValue categoryModeVal = content.getValue(N_CATEGORY_MODE, locale); 228 CombinationMode categoryMode = CombinationMode.OR; 229 if (categoryModeVal != null) { 230 try { 231 categoryMode = CombinationMode.valueOf(categoryModeVal.getStringValue(cms)); 232 } catch (Exception e) { 233 LOG.error(e.getLocalizedMessage(), e); 234 } 235 } 236 result.setCategoryMode(categoryMode); 237 238 LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>(); 239 for (I_CmsXmlContentValue parameter : content.getValues(N_PARAMETER, locale)) { 240 I_CmsXmlContentValue keyVal = content.getValue(parameter.getPath() + "/" + N_KEY, locale); 241 I_CmsXmlContentValue valueVal = content.getValue(parameter.getPath() + "/" + N_VALUE, locale); 242 if ((keyVal != null) 243 && CmsStringUtil.isNotEmptyOrWhitespaceOnly(keyVal.getStringValue(cms)) 244 && (valueVal != null)) { 245 parameters.put(keyVal.getStringValue(cms), valueVal.getStringValue(cms)); 246 } 247 } 248 result.setAdditionalParameters(parameters); 249 List<String> displayTypes = new ArrayList<String>(); 250 List<I_CmsXmlContentValue> typeValues = content.getValues(N_DISPLAY_TYPE, locale); 251 if (!typeValues.isEmpty()) { 252 for (I_CmsXmlContentValue value : typeValues) { 253 displayTypes.add(value.getStringValue(cms)); 254 } 255 } 256 result.setDisplayTypes(displayTypes); 257 List<String> folders = new ArrayList<String>(); 258 List<I_CmsXmlContentValue> folderValues = content.getValues(N_SEARCH_FOLDER, locale); 259 if (!folderValues.isEmpty()) { 260 for (I_CmsXmlContentValue value : folderValues) { 261 CmsLink val = ((CmsXmlVfsFileValue)value).getLink(cms); 262 if (val != null) { 263 // we are using root paths 264 folders.add(cms.getRequestContext().addSiteRoot(val.getSitePath(cms))); 265 } 266 } 267 } 268 result.setFolders(folders); 269 List<CmsUUID> blackList = new ArrayList<CmsUUID>(); 270 List<I_CmsXmlContentValue> blacklistValues = content.getValues(N_BLACKLIST, locale); 271 if (!blacklistValues.isEmpty()) { 272 for (I_CmsXmlContentValue value : blacklistValues) { 273 CmsLink link = ((CmsXmlVfsFileValue)value).getLink(cms); 274 if (link != null) { 275 blackList.add(link.getStructureId()); 276 } 277 } 278 } 279 result.setBlacklist(blackList); 280 List<I_CmsXmlContentValue> categoryFolderRestrictions = content.getValues( 281 N_CATEGORY_FOLDER_RESTRICTION, 282 locale); 283 if (!categoryFolderRestrictions.isEmpty()) { 284 for (I_CmsXmlContentValue restriction : categoryFolderRestrictions) { 285 List<String> restrictionFolders = new ArrayList<>(); 286 List<I_CmsXmlContentValue> folderVals = content.getValues( 287 CmsXmlUtils.concatXpath(restriction.getPath(), N_FOLDER), 288 locale); 289 for (I_CmsXmlContentValue folderVal : folderVals) { 290 CmsLink val = ((CmsXmlVfsFileValue)folderVal).getLink(cms); 291 if (val != null) { 292 // we are using root paths 293 restrictionFolders.add(cms.getRequestContext().addSiteRoot(val.getSitePath(cms))); 294 } 295 } 296 List<String> restrictionCategorySitePaths; 297 I_CmsXmlContentValue categoryVal = content.getValue( 298 CmsXmlUtils.concatXpath(restriction.getPath(), N_CATEGORY), 299 locale); 300 String categoryString = null != categoryVal ? categoryVal.getStringValue(cms) : ""; 301 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(categoryString)) { 302 restrictionCategorySitePaths = Arrays.asList(categoryString.split(",")); 303 } else { 304 restrictionCategorySitePaths = Collections.<String> emptyList(); 305 } 306 List<String> restrictionCategories = new ArrayList<>(restrictionCategorySitePaths.size()); 307 for (String sitePath : restrictionCategorySitePaths) { 308 try { 309 String path = CmsCategoryService.getInstance().getCategory( 310 cms, 311 cms.getRequestContext().addSiteRoot(sitePath)).getPath(); 312 restrictionCategories.add(path); 313 } catch (CmsException e) { 314 LOG.warn(e.getLocalizedMessage(), e); 315 } 316 } 317 String restrictionCategoryMode = content.getValue( 318 CmsXmlUtils.concatXpath(restriction.getPath(), N_CATEGORY_MODE), 319 locale).getStringValue(cms); 320 result.addCategoryFolderFilter( 321 new CmsCategoryFolderRestrictionBean( 322 restrictionCategories, 323 restrictionFolders, 324 null == restrictionCategoryMode ? null : CombinationMode.valueOf(restrictionCategoryMode))); 325 326 } 327 } 328 List<I_CmsXmlContentValue> preconfiguredRestrictions = content.getValues( 329 N_PRECONFIGURED_FILTER_QUERY, 330 locale); 331 if (!preconfiguredRestrictions.isEmpty()) { 332 CmsRestrictionsBean restrictionBean = new CmsRestrictionsBean(); 333 for (I_CmsXmlContentValue restriction : preconfiguredRestrictions) { 334 String restrictionRule = content.getValue( 335 CmsXmlUtils.concatXpath(restriction.getPath(), N_RULE), 336 locale).getStringValue(cms); 337 List<I_CmsXmlContentValue> restrictionVals = content.getValues( 338 CmsXmlUtils.concatXpath(restriction.getPath(), N_VALUE), 339 locale); 340 List<String> restrictionValues = restrictionVals.stream().map(v -> v.getStringValue(cms)).collect( 341 Collectors.toList()); 342 restrictionBean.addRestriction(restrictionRule, restrictionValues); 343 } 344 result.setPreconfiguredRestrictions(restrictionBean); 345 } 346 } catch (CmsException e) { 347 e.printStackTrace(); 348 } 349 return result; 350 } 351 352 /** 353 * Updates the black list entries in the provided xml content. 354 * @param cms the cms context. 355 * @param content the xml content to update (must be of type list_config) 356 * @param configBean the config bean to get the blacklist entries from. 357 * @return the updated content (update is in-place). 358 */ 359 public static CmsXmlContent updateBlackList( 360 CmsObject cms, 361 CmsXmlContent content, 362 CmsConfigurationBean configBean) { 363 364 // list configurations are single locale contents 365 Locale locale = CmsLocaleManager.MASTER_LOCALE; 366 int count = 0; 367 while (content.hasValue(N_BLACKLIST, locale)) { 368 content.removeValue(N_BLACKLIST, locale, 0); 369 } 370 for (CmsUUID hiddenId : configBean.getBlacklist()) { 371 CmsXmlVfsFileValue contentVal; 372 contentVal = (CmsXmlVfsFileValue)content.addValue(cms, N_BLACKLIST, locale, count); 373 contentVal.setIdValue(cms, hiddenId); 374 count++; 375 } 376 return content; 377 } 378 379 /** 380 * Creates the parameter map, mapping from noed names to parameters of the list configuration bean. 381 * @return the parameter map. 382 */ 383 private static Map<String, String> createParamsMap() { 384 385 Map<String, String> result = new HashMap<>(); 386 result.put(N_TITLE, CmsConfigurationBean.PARAM_TITLE); 387 result.put(N_FILTER_MULTI_DAY, CmsConfigurationBean.PARAM_FILTER_MULTI_DAY); 388 result.put(N_FILTER_QUERY, CmsConfigurationBean.PARAM_FILTER_QUERY); 389 result.put(N_SORT_ORDER, CmsConfigurationBean.PARAM_SORT_ORDER); 390 result.put(N_SHOW_EXPIRED, CmsConfigurationBean.PARAM_SHOW_EXPIRED); 391 result.put(N_MAX_RESULTS, CmsConfigurationBean.PARAM_MAX_RESULTS); 392 return Collections.unmodifiableMap(result); 393 } 394 395}