001/* 002 * Copyright 2010 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.web.repository.page.actions; 017 018import java.time.LocalDate; 019import java.time.ZoneId; 020import java.util.ArrayList; 021import java.util.Collections; 022import java.util.Date; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026import java.util.Optional; 027 028import org.apache.avalon.framework.parameters.ParameterException; 029import org.apache.avalon.framework.parameters.Parameters; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.cocoon.acting.ServiceableAction; 033import org.apache.cocoon.environment.ObjectModelHelper; 034import org.apache.cocoon.environment.Redirector; 035import org.apache.cocoon.environment.Request; 036import org.apache.cocoon.environment.SourceResolver; 037import org.apache.commons.lang3.ArrayUtils; 038import org.apache.commons.lang3.StringUtils; 039 040import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 041import org.ametys.cms.filter.ContentFilter; 042import org.ametys.cms.filter.ContentFilterExtensionPoint; 043import org.ametys.cms.repository.Content; 044import org.ametys.cms.tag.TagProviderExtensionPoint; 045import org.ametys.core.util.JSONUtils; 046import org.ametys.plugins.repository.AmetysObjectResolver; 047import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder; 048import org.ametys.plugins.repository.data.holder.group.impl.ModelAwareRepeater; 049import org.ametys.plugins.repository.data.holder.group.impl.ModelAwareRepeaterEntry; 050import org.ametys.plugins.repository.query.expression.AndExpression; 051import org.ametys.plugins.repository.query.expression.BooleanExpression; 052import org.ametys.plugins.repository.query.expression.DateExpression; 053import org.ametys.plugins.repository.query.expression.DoubleExpression; 054import org.ametys.plugins.repository.query.expression.Expression; 055import org.ametys.plugins.repository.query.expression.Expression.Operator; 056import org.ametys.plugins.repository.query.expression.ExpressionContext; 057import org.ametys.plugins.repository.query.expression.MetadataExpression; 058import org.ametys.plugins.repository.query.expression.NotExpression; 059import org.ametys.plugins.repository.query.expression.StringExpression; 060import org.ametys.plugins.repository.query.expression.StringWildcardExpression; 061import org.ametys.runtime.i18n.I18nizableText; 062import org.ametys.runtime.model.ElementDefinition; 063import org.ametys.web.WebConstants; 064import org.ametys.web.filter.DefaultWebContentFilter; 065import org.ametys.web.filter.WebContentFilter; 066import org.ametys.web.filter.WebContentFilter.AccessLimitation; 067import org.ametys.web.filter.WebContentFilter.Context; 068import org.ametys.web.filter.WebContentFilter.FilterSearchContext; 069import org.ametys.web.repository.page.Page; 070import org.ametys.web.repository.page.ZoneItem; 071import org.ametys.web.repository.site.SiteManager; 072import org.ametys.web.service.Service; 073import org.ametys.web.service.ServiceExtensionPoint; 074 075/** 076 * This action creates a filter from the sitemap parameters or request parameter 'filterId' and set the filter in request attributes. 077 * 078 */ 079@SuppressWarnings("deprecation") 080public class SetFilterInRequestAttributesAction extends ServiceableAction 081{ 082 private static final String __FILTERS_EQUAL = "ft-eq"; 083 private static final String __FILTERS_DIFFERENT = "ft-neq"; 084 private static final String __FILTERS_STARTSWITH = "ft-sw"; 085 private static final String __FILTERS_ENDSWITH = "ft-ew"; 086 private static final String __FILTERS_CONTAINS = "ft-co"; 087 private static final String __FILTERS_NOTCONTAINS = "ft-nco"; 088 private static final String __FILTERS_EMPTY = "ft-em"; 089 private static final String __FILTERS_NONEMPTY = "ft-nem"; 090 private static final String __FILTERS_LESSTHAN = "ft-lt"; 091 private static final String __FILTERS_GREATERTHAN = "ft-gt"; 092 private static final String __FILTERS_PAST = "ft-past"; 093 private static final String __FILTERS_FUTURE = "ft-future"; 094 private static final String __FILTERS_TRUE = "ft-true"; 095 private static final String __FILTERS_FALSE = "ft-false"; 096 097 /** The Ametys resolver */ 098 protected AmetysObjectResolver _resolver; 099 /** The extension point for content types */ 100 protected ContentTypeExtensionPoint _contentTypeEP; 101 /** The content filter EP */ 102 protected ContentFilterExtensionPoint _filterExtPt; 103 /** The service EP */ 104 protected ServiceExtensionPoint _serviceEP; 105 /** The site manager */ 106 protected SiteManager _siteManager; 107 /** The tag provider EP */ 108 protected TagProviderExtensionPoint _tagProviderEP; 109 /** The JSON utils */ 110 protected JSONUtils _jsonUtils; 111 112 @Override 113 public void service(ServiceManager smanager) throws ServiceException 114 { 115 super.service(smanager); 116 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 117 _contentTypeEP = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE); 118 _filterExtPt = (ContentFilterExtensionPoint) smanager.lookup(ContentFilterExtensionPoint.ROLE); 119 _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE); 120 _serviceEP = (ServiceExtensionPoint) smanager.lookup(ServiceExtensionPoint.ROLE); 121 _tagProviderEP = (TagProviderExtensionPoint) smanager.lookup(TagProviderExtensionPoint.ROLE); 122 _jsonUtils = (JSONUtils) smanager.lookup(JSONUtils.ROLE); 123 } 124 125 @Override 126 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 127 { 128 Map<String, String> result = new HashMap<>(); 129 130 Request request = ObjectModelHelper.getRequest(objectModel); 131 String filterId = parameters.getParameter("filterId", null); 132 133 ContentFilter filter = null; 134 if (filterId != null) 135 { 136 // Get the static filter from its id 137 filter = _getStaticFilterById(filterId, parameters.getParameter("siteName", "")); 138 } 139 else if (parameters.getParameter("zoneItemId", null) != null) 140 { 141 filter = _getFilterFromZoneItem(parameters, result); 142 } 143 else 144 { 145 @SuppressWarnings("unchecked") 146 Map<String, Object> parentContextAttributes = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT); 147 ZoneItem zi = (ZoneItem) request.getAttribute(WebConstants.REQUEST_ATTR_ZONEITEM); 148 filter = _getFilterFromParams(parameters, zi, parentContextAttributes); 149 } 150 151 // Set the filter in request attributes 152 if (filter != null) 153 { 154 request.setAttribute("filter", filter); 155 } 156 157 return result; 158 } 159 160 /** 161 * Get the filter from the action parameters. 162 * @param parameters The Action parameters. 163 * @param zoneItem the zone item 164 * @param parentContextAttributes The parent context attributes. 165 * @return the content filter. 166 */ 167 protected WebContentFilter _getFilterFromParams(Parameters parameters, ZoneItem zoneItem, Map<String, Object> parentContextAttributes) 168 { 169 // Creates filter from sitemap parameters 170 WebContentFilter filter = _createFilter(null, _resolver, _contentTypeEP, _siteManager, _tagProviderEP); 171 172 try 173 { 174 // The filter inputs 175 String[] cTypes; 176 String cTypesStr = parameters.getParameter("contentTypes", null); 177 if (cTypesStr == null) 178 { 179 cTypes = (String[]) parentContextAttributes.get("content-types"); 180 } 181 else 182 { 183 cTypes = StringUtils.split(cTypesStr, ','); 184 } 185 186 if (cTypes.length > 0 && cTypes[0].length() > 0) 187 { 188 for (String cType : cTypes) 189 { 190 if (StringUtils.isNotEmpty(cType)) 191 { 192 filter.addContentType(cType); 193 } 194 } 195 } 196 else if (zoneItem != null) 197 { 198 List<String> allCTypes = _getContentTypes (zoneItem); 199 for (String cType : allCTypes) 200 { 201 filter.addContentType(cType); 202 } 203 } 204 205 String searchContext = parameters.getParameter("searchContext", ""); 206 if (StringUtils.isNotEmpty(searchContext)) 207 { 208 _setSearchContext(filter, parameters); 209 } 210 else 211 { 212 @SuppressWarnings("unchecked") 213 List<Map<String, Object>> searchValues = (List<Map<String, Object>>) parentContextAttributes.get("search"); 214 _setSearchContext(filter, searchValues); 215 } 216 217 filter.setTitle(new I18nizableText(parameters.getParameter("service-title"))); 218 filter.setView(parameters.getParameter("viewName")); 219 filter.setLength(parameters.getParameterAsInteger("length", Integer.MAX_VALUE)); 220 221 // Filter criteria 222 Map<String, Object> filterBy = _getFilterMap(parameters, parentContextAttributes); 223 _setFilterCriteria(filter, filterBy); 224 225 // Sort criteria 226 List<Map<String, String>> sortBy = _getSortList(parameters, parentContextAttributes); 227 _setSortCriteria(filter, cTypes, sortBy); 228 229 // Mask orphan 230 boolean mask = parameters.getParameterAsBoolean("mask-orphan", false); 231 filter.setMaskOrphanContents(mask); 232 233 // Handle user access. 234 boolean handleUserAccess = parameters.getParameterAsBoolean("handle-user-access", false); 235 filter.setAccessLimitation(handleUserAccess ? AccessLimitation.USER_ACCESS : AccessLimitation.PAGE_ACCESS); 236 } 237 catch (ParameterException e) 238 { 239 getLogger().error("Missing at least one parameter", e); 240 } 241 242 return filter; 243 } 244 245 /** 246 * Retrieves the filter map in the parameters or parent context attribute 247 * @param parameters The parameters 248 * @param parentContextAttributes The parent context attributes 249 * @return The filter map 250 */ 251 protected Map<String, Object> _getFilterMap(Parameters parameters, Map<String, Object> parentContextAttributes) 252 { 253 String filterByAsString = parameters.getParameter("filterBy", ""); 254 if (StringUtils.isEmpty(filterByAsString)) 255 { 256 filterByAsString = (String) parentContextAttributes.get("filterBy"); 257 } 258 259 if (StringUtils.isNotEmpty(filterByAsString)) 260 { 261 return _jsonUtils.convertJsonToMap(filterByAsString); 262 } 263 else 264 { 265 return null; 266 } 267 } 268 269 /** 270 * Retrieves the filter map from a data holder (service parameters) 271 * @param dataHolder The data holder 272 * @return The filter map 273 */ 274 protected Map<String, Object> _getFilterMap(ModelAwareDataHolder dataHolder) 275 { 276 Map<String, Object> filterBy = null; 277 278 String value = dataHolder.getValue("filterBy", false, ""); 279 if (StringUtils.isNotBlank(value)) 280 { 281 filterBy = _jsonUtils.convertJsonToMap(value); 282 } 283 284 return filterBy; 285 } 286 287 /** 288 * Retrieves the sort list in the parameters or parent context attribute 289 * @param parameters The parameters 290 * @param parentContextAttributes The parent context attributes 291 * @return The sort list 292 */ 293 @SuppressWarnings("unchecked") 294 protected List<Map<String, String>> _getSortList(Parameters parameters, Map<String, Object> parentContextAttributes) 295 { 296 String sortByAsString = parameters.getParameter("sortBy", ""); 297 if (StringUtils.isEmpty(sortByAsString)) 298 { 299 sortByAsString = (String) parentContextAttributes.get("sortBy"); 300 } 301 302 if (StringUtils.isNotEmpty(sortByAsString)) 303 { 304 List<Map<String, String>> sortBy = new ArrayList<>(); 305 for (Object entry : _jsonUtils.convertJsonToList(sortByAsString)) 306 { 307 sortBy.add((Map<String, String>) entry); 308 } 309 return sortBy; 310 } 311 else 312 { 313 return null; 314 } 315 } 316 317 /** 318 * Retrieves the sort list map from a data holder (service parameters) 319 * @param metadata The data holder 320 * @return The filter map 321 */ 322 @SuppressWarnings("unchecked") 323 protected List<Map<String, String>> _getSortList(ModelAwareDataHolder metadata) 324 { 325 List<Map<String, String>> sortBy = null; 326 327 String value = metadata.getValue("sortBy", false, ""); 328 if (StringUtils.isNotBlank(value)) 329 { 330 sortBy = new ArrayList<>(); 331 for (Object entry : _jsonUtils.convertJsonToList(value)) 332 { 333 sortBy.add((Map<String, String>) entry); 334 } 335 } 336 337 return sortBy; 338 } 339 340 private List<String> _getContentTypes (ZoneItem zoneItem) throws ParameterException 341 { 342 List<String> contentTypes = new ArrayList<>(); 343 344 String serviceId = zoneItem.getServiceId(); 345 Service service = _serviceEP.getExtension(serviceId); 346 347 if (service.hasModelItem("content-types")) 348 { 349 @SuppressWarnings("unchecked") 350 ElementDefinition<String> contentTypesParam = (ElementDefinition<String>) service.getModelItem("content-types"); 351 352 try 353 { 354 Map<String, I18nizableText> entries = contentTypesParam.getEnumerator().getTypedEntries(); 355 for (String contentType : entries.keySet()) 356 { 357 contentTypes.add(contentType); 358 } 359 360 return contentTypes; 361 } 362 catch (Exception e) 363 { 364 throw new ParameterException("Unable to get the list of available content types from service parameters", e); 365 } 366 } 367 368 return contentTypes; 369 370 } 371 372 /** 373 * Get the content filter from a ZoneItem. 374 * @param parameters the action parameters, including the ZoneItem id. 375 * @param result the action result. 376 * @return the content filter. 377 * @throws ParameterException if parameters are invalid 378 */ 379 protected WebContentFilter _getFilterFromZoneItem(Parameters parameters, Map<String, String> result) throws ParameterException 380 { 381 WebContentFilter filter = null; 382 383 String zoneItemId = parameters.getParameter("zoneItemId"); 384 String protocol = parameters.getParameter("protocol"); 385 386 ZoneItem zoneItem = _resolver.resolveById(protocol + "://" + zoneItemId); 387 if (_isValid(zoneItem)) 388 { 389 // Creates filter from services parameters 390 filter = _getFilterFromZoneItem(zoneItem); 391 } 392 393 Page page = zoneItem.getZone().getPage(); 394 result.put("siteName", page.getSiteName()); 395 result.put("lang", page.getSitemapName()); 396 result.put("path", page.getPathInSitemap()); 397 398 return filter; 399 } 400 401 /** 402 * Get the content filter from a ZoneItem. 403 * @param zoneItem the ZoneItem. 404 * @return the content filter. 405 * @throws ParameterException if parameters are invalid 406 */ 407 protected WebContentFilter _getFilterFromZoneItem(ZoneItem zoneItem) throws ParameterException 408 { 409 WebContentFilter filter = _createFilter(null, _resolver, _contentTypeEP, _siteManager, _tagProviderEP); 410 411 ModelAwareDataHolder dataHolder = zoneItem.getServiceParameters(); 412 413 String[] contentTypes = dataHolder.getValue("content-types"); 414 if (contentTypes.length > 0 && contentTypes[0].length() > 0) 415 { 416 for (String contentType : contentTypes) 417 { 418 if (!StringUtils.isEmpty(contentType)) 419 { 420 filter.addContentType(contentType); 421 } 422 } 423 } 424 else 425 { 426 List<String> allContentTypes = _getContentTypes(zoneItem); 427 for (String contentType : allContentTypes) 428 { 429 filter.addContentType(contentType); 430 } 431 } 432 433 filter.setView(dataHolder.getValue("metadataSetName")); 434 filter.setLength(Math.toIntExact(dataHolder.getValue("nb-max", true, Integer.valueOf(Integer.MAX_VALUE).longValue()))); 435 filter.setTitle(new I18nizableText(dataHolder.getValue("header"))); 436 437 // Contexts 438 _setSearchContext(filter, dataHolder); 439 440 Map<String, Object> filterBy = _getFilterMap(dataHolder); 441 _setFilterCriteria(filter, filterBy); 442 443 // Sort criteria 444 List<Map<String, String>> sortBy = _getSortList(dataHolder); 445 _setSortCriteria(filter, contentTypes, sortBy); 446 447 // Mask orphan 448 boolean maskOrphan = dataHolder.getValue("mask-orphan", false, false); 449 filter.setMaskOrphanContents(maskOrphan); 450 451 // Handle user access 452 boolean handleUserAccess = dataHolder.getValue("handle-user-access", false, false); 453 filter.setAccessLimitation(handleUserAccess ? AccessLimitation.USER_ACCESS : AccessLimitation.PAGE_ACCESS); 454 455 return filter; 456 } 457 458 /** 459 * Create a WebContentFilter. 460 * @param id the filter ID. 461 * @param resolver the AmetysObjectResolver. 462 * @param contentTypeExtensionPoint The extension point for content types 463 * @param siteManager The site manager 464 * @param tagProviderEP The tag provider extension point 465 * @return the WebContentFilter. 466 */ 467 protected WebContentFilter _createFilter(String id, AmetysObjectResolver resolver, ContentTypeExtensionPoint contentTypeExtensionPoint, SiteManager siteManager, TagProviderExtensionPoint tagProviderEP) 468 { 469 return new DefaultWebContentFilter(id, resolver, contentTypeExtensionPoint, siteManager, tagProviderEP); 470 } 471 472 /** 473 * Test if the ZoneItem is valid. 474 * @param zoneItem the ZoneItem. 475 * @return true if the ZoneItem is valid, false otherwise. 476 */ 477 protected boolean _isValid(ZoneItem zoneItem) 478 { 479 return zoneItem.getType().equals(ZoneItem.ZoneType.SERVICE) 480 && "org.ametys.web.service.FilteredContentsService".equals(zoneItem.getServiceId()); 481 } 482 483 /** 484 * Get the search parameters from the search values as parameters. 485 * @param filter the filter to configure. 486 * @param parameters the parameters. 487 * @throws ParameterException if an error occurs. 488 */ 489 protected void _setSearchContext(WebContentFilter filter, Parameters parameters) throws ParameterException 490 { 491 FilterSearchContext filterContext = filter.addSearchContext(); 492 493 String searchContext = parameters.getParameter("searchContext"); 494 if ("DIRECT_CHILD_PAGES".equals(searchContext)) 495 { 496 // Direct child pages are child pages context with depth 1. 497 filterContext.setContext(Context.CHILD_PAGES); 498 filterContext.setDepth(1); 499 } 500 else 501 { 502 // Else, parse the context. 503 filterContext.setContext(Context.valueOf(searchContext)); 504 } 505 506 String sitesAsStr = parameters.getParameter("sites", ""); 507 if (sitesAsStr.length() > 0) 508 { 509 String[] sites = sitesAsStr.split(","); 510 for (String site : sites) 511 { 512 filterContext.addSite(site); 513 } 514 } 515 516 if (!"".equals(parameters.getParameter("contextLang"))) 517 { 518 filterContext.setContextLanguage(ContentFilter.ContextLanguage.valueOf(parameters.getParameter("contextLang"))); 519 } 520 521 String tagAsStr = parameters.getParameter("tags", ""); 522 if (tagAsStr.length() > 0) 523 { 524 String[] tags = tagAsStr.split(","); 525 for (String tag : tags) 526 { 527 filterContext.addTag(tag); 528 } 529 } 530 531 if (parameters.isParameter("strict-search-on-tags")) 532 { 533 boolean strictSearchOnTags = parameters.getParameterAsBoolean("strict-search-on-tags"); 534 filterContext.setTagsAutoPosting(!strictSearchOnTags); 535 } 536 } 537 538 /** 539 * Get the search parameters from the search values. 540 * @param filter the filter to configure. 541 * @param searchValues the search values. 542 */ 543 protected void _setSearchContext(WebContentFilter filter, List<Map<String, Object>> searchValues) 544 { 545 List<Map<String, Object>> nonNullSearchValues = Optional.ofNullable(searchValues) 546 .orElse(Collections.EMPTY_LIST); 547 548 for (Map<String, Object> entry : nonNullSearchValues) 549 { 550 FilterSearchContext filterContext = filter.addSearchContext(); 551 552 Map<String, Object> sitesData = _jsonUtils.convertJsonToMap((String) entry.get("sites")); 553 String searchContext = (String) sitesData.get("context"); 554 555 Map<String, Object> searchContextMap = _jsonUtils.convertJsonToMap((String) entry.get("search-context")); 556 String subSearchContext = (String) searchContextMap.get("context"); 557 if (StringUtils.isEmpty(searchContext) 558 || subSearchContext != null && !Context.CURRENT_SITE.name().equals(subSearchContext)) 559 { 560 // Delegate to the search-context enumeration context 561 searchContext = subSearchContext; 562 } 563 564 if ("CHILD_PAGES".equals(searchContext) || "CHILD_PAGES_OF".equals(searchContext)) 565 { 566 filterContext.setContext(Context.CHILD_PAGES); 567 } 568 else if ("DIRECT_CHILD_PAGES".equals(searchContext) || "DIRECT_CHILD_PAGES_OF".equals(searchContext)) 569 { 570 // Direct child pages are child pages context with depth 1. 571 filterContext.setContext(Context.CHILD_PAGES); 572 filterContext.setDepth(1); 573 } 574 else 575 { 576 // Else, parse the context. 577 filterContext.setContext(Context.valueOf(searchContext)); 578 } 579 580 if (searchContextMap.get("page") != null) 581 { 582 filterContext.setPageId((String) searchContextMap.get("page")); 583 } 584 585 @SuppressWarnings("unchecked") 586 List<String> sites = Optional.of("sites") 587 .map(sitesData::get) 588 .map(s -> (List<String>) s) 589 .orElse(Collections.EMPTY_LIST); 590 for (String site : sites) 591 { 592 filterContext.addSite(site); 593 } 594 595 String contextLang = (String) entry.get("context-lang"); 596 if (StringUtils.isNotEmpty(contextLang)) 597 { 598 filterContext.setContextLanguage(ContentFilter.ContextLanguage.valueOf(contextLang)); 599 } 600 601 String[] tags = Optional.of("tags") 602 .map(entry::get) 603 .map(t -> (String[]) t) 604 .orElse(new String[0]); 605 for (String tag : tags) 606 { 607 filterContext.addTag(tag); 608 } 609 610 if (entry.containsKey("strict-search-on-tags")) 611 { 612 boolean strictSearchOnTags = (boolean) entry.get("strict-search-on-tags"); 613 filterContext.setTagsAutoPosting(!strictSearchOnTags); 614 } 615 } 616 } 617 618 /** 619 * Set the search contexts in a filter from a service instance attributes. 620 * @param filter the filter. 621 * @param dataHolder the service parameters data holder. 622 */ 623 protected void _setSearchContext(WebContentFilter filter, ModelAwareDataHolder dataHolder) 624 { 625 ModelAwareRepeater searchRepeater = dataHolder.getRepeater("search"); 626 627 for (ModelAwareRepeaterEntry searchRepeaterEntry : searchRepeater.getEntries()) 628 { 629 FilterSearchContext filterContext = filter.addSearchContext(); 630 631 Map<String, Object> sitesData = _jsonUtils.convertJsonToMap(searchRepeaterEntry.getValue("sites", false, "")); 632 String searchContext = (String) sitesData.get("context"); 633 634 Map<String, Object> searchContextMap = _jsonUtils.convertJsonToMap(searchRepeaterEntry.getValue("search-context", false, "")); 635 String subSearchContext = (String) searchContextMap.get("context"); 636 if (StringUtils.isEmpty(searchContext) 637 || subSearchContext != null && !Context.CURRENT_SITE.name().equals(subSearchContext)) 638 { 639 // Delegate to the search-context enumeration context 640 searchContext = subSearchContext; 641 } 642 643 if ("CHILD_PAGES".equals(searchContext) || "CHILD_PAGES_OF".equals(searchContext)) 644 { 645 filterContext.setContext(Context.CHILD_PAGES); 646 } 647 else if ("DIRECT_CHILD_PAGES".equals(searchContext) || "DIRECT_CHILD_PAGES_OF".equals(searchContext)) 648 { 649 // Direct child pages are child pages context with depth 1. 650 filterContext.setContext(Context.CHILD_PAGES); 651 filterContext.setDepth(1); 652 } 653 else 654 { 655 // Else, parse the context. 656 filterContext.setContext(Context.valueOf(searchContext)); 657 } 658 659 if (searchContextMap.get("page") != null) 660 { 661 filterContext.setPageId((String) searchContextMap.get("page")); 662 } 663 664 @SuppressWarnings("unchecked") 665 List<String> sites = Optional.of("sites") 666 .map(sitesData::get) 667 .map(s -> (List<String>) s) 668 .orElse(Collections.EMPTY_LIST); 669 for (String site : sites) 670 { 671 filterContext.addSite(site); 672 } 673 674 String contextLang = searchRepeaterEntry.getValue("context-lang", false, ""); 675 if (StringUtils.isNotEmpty(contextLang)) 676 { 677 filterContext.setContextLanguage(ContentFilter.ContextLanguage.valueOf(contextLang)); 678 } 679 680 String[] tags = searchRepeaterEntry.getValue("tags", false, ArrayUtils.EMPTY_STRING_ARRAY); 681 for (String tag : tags) 682 { 683 filterContext.addTag(tag); 684 } 685 686 if (searchRepeaterEntry.hasValue("strict-search-on-tags")) 687 { 688 boolean strictSearchOnTags = searchRepeaterEntry.getValue("strict-search-on-tags"); 689 filterContext.setTagsAutoPosting(!strictSearchOnTags); 690 } 691 } 692 } 693 694 /** 695 * Set the filters's filter expression 696 * @param contentFilter The filter to edit 697 * @param filterBy The map of filters. Key are the medataname. Value represents the filter. 698 */ 699 protected void _setFilterCriteria(ContentFilter contentFilter, Map<String, Object> filterBy) 700 { 701 if (filterBy != null && !filterBy.isEmpty()) 702 { 703 List<Expression> exprs = new ArrayList<>(); 704 705 for (String metadataName : filterBy.keySet()) 706 { 707 String[] filter = StringUtils.split((String) filterBy.get(metadataName), '_'); 708 709 String filterName = filter[0]; 710 boolean lowerCase = filter.length >= 2 && "LC".equals(filter[1]); 711 String filterValue = filter.length >= 3 ? filter[2] : null; 712 713 Expression expr = _convertToExpression(metadataName, lowerCase, filterName, filterValue); 714 exprs.add(expr); 715 } 716 717 Expression[] exprsAsArray = new Expression[exprs.size()]; 718 exprs.toArray(exprsAsArray); 719 Expression expr = new AndExpression(exprsAsArray); 720 contentFilter.setAdditionalFilterExpression(expr); 721 } 722 } 723 724 private Expression _convertToExpression(String metadataName, boolean lowerCase, String metadataCondition, String metadataConditionValue) 725 { 726 ExpressionContext exprContext = ExpressionContext.newInstance().withCaseInsensitive(lowerCase); 727 if (__FILTERS_EQUAL.equals(metadataCondition)) 728 { 729 return new StringExpression(metadataName, Operator.EQ, metadataConditionValue, exprContext); 730 } 731 else if (__FILTERS_DIFFERENT.equals(metadataCondition)) 732 { 733 return new StringExpression(metadataName, Operator.NE, metadataName, exprContext); 734 } 735 else if (__FILTERS_STARTSWITH.equals(metadataCondition)) 736 { 737 // TODO CMS-3881 738 return new StringWildcardExpression(metadataName, metadataConditionValue, exprContext); 739 } 740 else if (__FILTERS_ENDSWITH.equals(metadataCondition)) 741 { 742 // TODO CMS-3881 743 return new StringWildcardExpression(metadataName, metadataConditionValue, exprContext); 744 } 745 else if (__FILTERS_CONTAINS.equals(metadataCondition)) 746 { 747 // TODO CMS-3881 748 return new StringWildcardExpression(metadataName, metadataConditionValue, exprContext); 749 } 750 else if (__FILTERS_NOTCONTAINS.equals(metadataCondition)) 751 { 752 // TODO CMS-3881 753 return new NotExpression(new StringWildcardExpression(metadataName, metadataConditionValue, exprContext)); 754 } 755 else if (__FILTERS_EMPTY.equals(metadataCondition)) 756 { 757 return new NotExpression(new MetadataExpression(metadataName)); 758 } 759 else if (__FILTERS_NONEMPTY.equals(metadataCondition)) 760 { 761 return new MetadataExpression(metadataName); 762 } 763 else if (__FILTERS_LESSTHAN.equals(metadataCondition)) 764 { 765 return new DoubleExpression(metadataName, Operator.LE, Double.parseDouble(metadataConditionValue)); 766 } 767 else if (__FILTERS_GREATERTHAN.equals(metadataCondition)) 768 { 769 return new DoubleExpression(metadataName, Operator.GE, Double.parseDouble(metadataConditionValue)); 770 } 771 else if (__FILTERS_PAST.equals(metadataCondition)) 772 { 773 774 return new DateExpression(metadataName, Operator.LE, Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant())); 775 } 776 else if (__FILTERS_FUTURE.equals(metadataCondition)) 777 { 778 return new DateExpression(metadataName, Operator.GE, Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant())); 779 } 780 else if (__FILTERS_TRUE.equals(metadataCondition)) 781 { 782 return new BooleanExpression(metadataName, true); 783 } 784 else if (__FILTERS_FALSE.equals(metadataCondition)) 785 { 786 return new BooleanExpression(metadataName, false); 787 } 788 else 789 { 790 getLogger().error("Unknown condition '" + metadataCondition + "'"); 791 return null; 792 } 793 } 794 795 /** 796 * Set the filter's sort criteria. 797 * @param filter The filter to edit 798 * @param cTypes The content types 799 * @param sortBy The sort list. Each entry in the list is a map representing a sort. Map have two keys: name, the name of the metadata, and sort, the name of the sort 800 */ 801 protected void _setSortCriteria(ContentFilter filter, String[] cTypes, List<Map<String, String>> sortBy) 802 { 803 if (sortBy != null && !sortBy.isEmpty()) 804 { 805 for (Map<String, String> sortEntry : sortBy) 806 { 807 String[] sort = StringUtils.split(sortEntry.get("sort"), '_'); 808 809 boolean ascending = sort == null || "ASC".equals(sort[0]); 810 boolean useLowerCase = sort != null && sort.length >= 2 && "LC".equals(sort[1]); 811 812 filter.addSortCriteria(sortEntry.get("name"), ascending, useLowerCase); 813 } 814 } 815 else 816 { 817 filter.addSortCriteria(Content.ATTRIBUTE_TITLE, true, true); 818 } 819 } 820 821 /** 822 * Retrieve the content filter with its id 823 * @param filterId the id of the filter to retrieve 824 * @param siteName The site name 825 * @return the content filter associated with this id 826 */ 827 protected ContentFilter _getStaticFilterById(String filterId, String siteName) 828 { 829 return _filterExtPt.getExtension(filterId); 830 } 831}