@NotNull @Override public Optional<Collection<SitemapItemModelData>> getNavigationSubtree(@NotNull SitemapRequestDto requestDto) { try { List<SitemapItemModelData> models = Arrays.asList(modelServiceClient.getForType(configuration.getOnDemandApiUrl(), SitemapItemModelData[].class, requestDto.getLocalizationId(), requestDto.getSitemapId(), requestDto.getNavigationFilter().isWithAncestors(), requestDto.getNavigationFilter().getDescendantLevels())); return Optional.of(models); } catch (ItemNotFoundInModelServiceException e) { log.warn("Cannot find items for on-demand dynamic navigation from the MS for the request {}", requestDto, e); return Optional.empty(); } } }
public static SitemapRequestDtoBuilder builder(int localizationId) { return hiddenBuilder().localizationId(localizationId); }
@NotNull private Optional<SitemapItemModelData> _getNavigationModel(Localization localization) { SitemapRequestDto requestDto = SitemapRequestDto.wholeTree(Integer.parseInt(localization.getId())).build(); Optional<TaxonomyNodeModelData> navigationModel = navigationModelProvider.getNavigationModel(requestDto); if (!navigationModel.isPresent()) { log.warn("Taxonomy navigation is not available, fallback to static navigation is required, localizationId {}", localization.getId()); return Optional.empty(); } Assert.isInstanceOf(TaxonomyNodeModelData.class, navigationModel.get(), "Navigation model should always be a taxonomy node"); return Optional.of(navigationModel.get()); }
log.trace("Original sitemapRequestDto {}", requestDto); SitemapRequestDto request = requestDto.toBuilder() .expandLevels(new DepthCounter(requestDto.getNavigationFilter().getDescendantLevels())) .build(); if (isNullOrEmpty(request.getSitemapId())) { if(request.getNavigationFilter().getDescendantLevels() != 0) { log.trace("Sitemap ID is empty, expanding all taxonomy roots"); SitemapRequestDto adaptedRequest = request.nextExpandLevel(); TaxonomyUrisHolder info = parse(request.getSitemapId(), request.getLocalizationId()); if (info == null) { throw new BadRequestException(String.format("SitemapID %s is wrong for Taxonomy navigation", request.getSitemapId())); if (request.getNavigationFilter().isWithAncestors()) { log.trace("Filter with ancestors, expanding ancestors"); Optional<SitemapItemModelData> taxonomy = taxonomyWithAncestors(info, request); if (request.getNavigationFilter().getDescendantLevels() != 0 && !info.isPage()) { log.trace("Filter with descendants, expanding descendants"); return expandDescendants(info, request);
/** * Expands root Taxonomies. * * @param requestDto current request with mandatory localization ID and navigation filter * @param filter way to filter roots, pass empty predicate always returning true {@code () -> true} * @return a list of root Taxonomies */ @NotNull private List<Keyword> getTaxonomyRoots(@NotNull SitemapRequestDto requestDto, @NotNull() Predicate<Keyword> filter) { Assert.notNull(requestDto.getLocalizationId(), "Localization ID is required to load taxonomy roots"); Assert.notNull(requestDto.getNavigationFilter(), "Navigation Filter is required to load taxonomy roots"); NavigationFilter navigationFilter = requestDto.getNavigationFilter(); // since we load categories here, we have to decrease depth by one because the first level is categories level // and we want top-level keywords final int maximumDepth = navigationFilter.getDescendantLevels() > 0 ? navigationFilter.getDescendantLevels() - 1 : navigationFilter.getDescendantLevels(); TaxonomyFilter depthFilter = new DepthFilter(maximumDepth, DepthFilter.FILTER_DOWN); return Arrays.stream(taxonomyFactory.getTaxonomies(TcmUtils.buildPublicationTcmUri(requestDto.getLocalizationId()))) .distinct() .map(taxonomy -> taxonomyFactory.getTaxonomyKeywords(taxonomy, depthFilter)) .filter(filter) .collect(Collectors.toList()); }
@Override @Cacheable(value = "sitemaps", key = "{ #root.methodName, #requestDto }") public Optional<TaxonomyNodeModelData> getNavigationModel(@NotNull SitemapRequestDto requestDto) { Assert.notNull(requestDto.getLocalizationId(), "Localization ID is required to load dynamic navigation"); List<Keyword> roots = getTaxonomyRoots(requestDto, keyword -> keyword.getKeywordName().contains(taxonomyNavigationMarker)); if (roots.isEmpty()) { log.error("No Navigation Taxonomy Found in Localization [{}]. Ensure a Taxonomy with '{}' in its title is published", requestDto.getLocalizationId(), taxonomyNavigationMarker); return Optional.empty(); } Keyword rootTaxonomy = roots.get(0); log.debug("Resolved Navigation Taxonomy {} for request {}", rootTaxonomy, requestDto); return Optional.of(createTaxonomyNode(rootTaxonomy, requestDto)); }
private TaxonomyNodeModelData createTaxonomyNode(@NotNull Keyword keyword, @NotNull SitemapRequestDto requestDto) { log.debug("Creating taxonomy node for keyword {} and request {}", keyword.getTaxonomyURI(), requestDto); String taxonomyId = String.valueOf(TcmUtils.getItemId(keyword.getTaxonomyURI())); String taxonomyNodeUrl = null; List<SitemapItemModelData> children = new ArrayList<>(); if (requestDto.getExpandLevels().isNotTooDeep()) { keyword.getKeywordChildren().forEach(child -> children.add(createTaxonomyNode(child, requestDto.nextExpandLevel()))); if (keyword.getReferencedContentCount() > 0 && requestDto.getNavigationFilter().getDescendantLevels() != 0) { List<SitemapItemModelData> pageSitemapItems = getChildrenPages(keyword, taxonomyId, requestDto); taxonomyNodeUrl = findIndexPageUrl(pageSitemapItems).orElse(null); log.trace("taxonomyNodeUrl = {}", taxonomyNodeUrl); children.addAll(pageSitemapItems); } } children.forEach(child -> child.setTitle(removeSequenceFromPageTitle(child.getTitle()))); return createTaxonomyNodeFromKeyword(keyword, taxonomyId, taxonomyNodeUrl, new TreeSet<>(children)); }
if (requestDto.getNavigationFilter().getDescendantLevels() != 0) { addDescendantsToTaxonomy(taxonomy.get(), requestDto);
/** * Ancestors for a page is a list of same ROOT node with different children. * Basically, these different ROOTs (with same ID, because we are still within one taxonomy) contain * different children for different paths your page may be in. * <p>Unless other methods for descendants, this method returns {@link List} because the root Taxonomies will be the same object * even if page is in multiple places.</p> * * @param uris URIs of your current context taxonomy node * @param requestDto current request data * @return a list of roots of taxonomy with different paths for items */ @NotNull private List<SitemapItemModelData> collectAncestorsForPage(@NotNull TaxonomyUrisHolder uris, @NotNull SitemapRequestDto requestDto) { if (!uris.isPage()) { throw new IllegalArgumentException(String.format("Method for pages was called for not a page! uris: %s, request: %s", uris, requestDto)); } TaxonomyFilter depthFilter = new DepthFilter(DepthFilter.UNLIMITED_DEPTH, DepthFilter.FILTER_UP); Keyword[] keywords = getRelationTaxonomyKeywords(uris, depthFilter); if (keywords == null || keywords.length == 0) { log.debug("Page {} is not classified in taxonomy {}", uris.getPageUri(), uris.getTaxonomyUri()); return Collections.emptyList(); } return Arrays.stream(keywords) .map(keyword -> createTaxonomyNode(keyword, requestDto.toBuilder().expandLevels(DepthCounter.UNLIMITED_DEPTH).build())) .collect(Collectors.toList()); }
@Override public Collection<SitemapItem> getNavigationSubtree(@Nullable String sitemapItemId, @NonNull NavigationFilter navigationFilter, @NonNull Localization localization) { Optional<Collection<SitemapItemModelData>> subtree; SitemapRequestDto requestDto = SitemapRequestDto .builder(Integer.parseInt(localization.getId())) .navigationFilter(navigationFilter) .expandLevels(new DepthCounter(navigationFilter.getDescendantLevels())) .sitemapId(sitemapItemId) .build(); subtree = onDemandNavigationModelProvider.getNavigationSubtree(requestDto); if (!subtree.isPresent()) { log.debug("Nothing found for the given request {}", requestDto); return Collections.emptyList(); } return subtree.get().stream() .map(this::_convert) .collect(Collectors.toList()); }
private List<SitemapItemModelData> getChildrenPages(@NotNull Keyword keyword, @NotNull String taxonomyId, @NotNull SitemapRequestDto requestDto) { log.trace("Getting SitemapItems for all classified Pages (ordered by Page Title, including sequence prefix if any), " + "keyword {}, taxonomyId {}, localization {}", keyword, taxonomyId, requestDto.getLocalizationId()); List<SitemapItemModelData> items = new ArrayList<>(); try { PageMetaFactory pageMetaFactory = new PageMetaFactory(requestDto.getLocalizationId()); PageMeta[] taxonomyPages = pageMetaFactory.getTaxonomyPages(keyword, false); items = Arrays.stream(taxonomyPages) .map(page -> createSitemapItemFromPage(page, taxonomyId)) .collect(Collectors.toList()); } catch (StorageException e) { log.error("Error loading taxonomy pages for taxonomyId = {}, localizationId = {} and keyword {}", taxonomyId, requestDto.getLocalizationId(), keyword, e); } return items; }
/** * Loads taxonomy and expands descendants for a given taxonomy URI and basing on a current request. * * @param uris information about URI of current item * @param requestDto current request data * @return an optional collection of descendants of item with passed URI */ @NotNull private Optional<Collection<SitemapItemModelData>> expandDescendants(TaxonomyUrisHolder uris, @NotNull SitemapRequestDto requestDto) { if (uris.isPage()) { String message = "Page cannot have descendants, uris = " + uris; log.warn(message); throw new BadRequestException(message); } Keyword keyword = taxonomyFactory.getTaxonomyKeywords(uris.getTaxonomyUri(), new DepthFilter(requestDto.getNavigationFilter().getDescendantLevels(), DepthFilter.FILTER_DOWN), uris.getKeywordUri()); if (keyword == null) { log.warn("Keyword '{}' in Taxonomy '{}' was not found.", uris.getKeywordUri(), uris.getTaxonomyUri()); return Optional.empty(); } return Optional.of(createTaxonomyNode(keyword, requestDto).getItems()); }
public synchronized SitemapRequestDto nextExpandLevel() { return this.toBuilder().expandLevels(new DepthCounter(expandLevels.getCounter() - 1)).build(); }
public static SitemapRequestDtoBuilder wholeTree(int localizationId) { return builder(localizationId) .navigationFilter(new NavigationFilter().setDescendantLevels(-1)) .expandLevels(DepthCounter.UNLIMITED_DEPTH); }
@NotNull @Override public Optional<Collection<SitemapItemModelData>> getNavigationSubtree(@NotNull SitemapRequestDto requestDto) { try { List<SitemapItemModelData> models = Arrays.asList(modelServiceClient.getForType(configuration.getOnDemandApiUrl(), SitemapItemModelData[].class, requestDto.getLocalizationId(), requestDto.getSitemapId(), requestDto.getNavigationFilter().isWithAncestors(), requestDto.getNavigationFilter().getDescendantLevels())); return Optional.of(models); } catch (DxaItemNotFoundException e) { log.warn("Cannot find items for on-demand dynamic navigation from the MS for the request {}", requestDto, e); return Optional.empty(); } } }
@Override public Optional<TaxonomyNodeModelData> getNavigationModel(@NotNull SitemapRequestDto requestDto) { try { return Optional.of(modelServiceClient.getForType(configuration.getNavigationApiUrl(), TaxonomyNodeModelData.class, requestDto.getLocalizationId())); } catch (DxaItemNotFoundException e) { log.warn("Cannot find a dynamic navigation in the MS for the request {}", requestDto, e); return Optional.empty(); } }
/** * One single ancestor for a given keyword. Although same keyword may be in few places, we don't expect it due to * technical limitation in CME. So basically we ignore the fact that keyword may be in many places (like page) and * expect only a single entry. Because of that we have only one taxonomy root for Keyword's ancestors. * * @param uris URIs of your current context taxonomy node * @param requestDto current request data * @return root of a taxonomy */ @NotNull private Optional<SitemapItemModelData> expandAncestorsForKeyword(TaxonomyUrisHolder uris, SitemapRequestDto requestDto) { if (!uris.isKeyword()) { throw new IllegalArgumentException(String.format("Method for keywords was called for not a keyword! uris: %s, request: %s", uris, requestDto)); } TaxonomyFilter depthFilter = new DepthFilter(DepthFilter.UNLIMITED_DEPTH, DepthFilter.FILTER_UP); Keyword taxonomyRoot = taxonomyFactory.getTaxonomyKeywords(uris.getTaxonomyUri(), depthFilter, uris.getKeywordUri()); if (taxonomyRoot == null) { log.warn("Keyword {} in taxonomy {} wasn't found", uris.getKeywordUri(), uris.getTaxonomyUri()); return Optional.empty(); } return Optional.of(createTaxonomyNode(taxonomyRoot, requestDto.toBuilder().expandLevels(DepthCounter.UNLIMITED_DEPTH).build())); }
@Override public Optional<TaxonomyNodeModelData> getNavigationModel(@NotNull SitemapRequestDto requestDto) { try { return Optional.of(modelServiceClient.getForType(configuration.getNavigationApiUrl(), TaxonomyNodeModelData.class, requestDto.getLocalizationId())); } catch (ItemNotFoundInModelServiceException e) { log.warn("Cannot find a dynamic navigation in the MS for the request {}", requestDto, e); return Optional.empty(); } }
private void addDescendantsToTaxonomy(@NonNull SitemapItemModelData taxonomy, @NotNull SitemapRequestDto requestDto) { taxonomy.getItems().stream() .filter(TaxonomyNodeModelData.class::isInstance) .forEach(child -> addDescendantsToTaxonomy(child, requestDto)); TaxonomyUrisHolder uris = parse(taxonomy.getId(), requestDto.getLocalizationId()); Set<SitemapItemModelData> children = new LinkedHashSet<>(expandDescendants(uris, requestDto).orElse(Collections.emptyList())); for (SitemapItemModelData child : difference(children, newHashSet(taxonomy.getItems()))) { taxonomy.addItem(child); } }