All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sdl.webapp.tridion.navigation.DynamicNavigationProvider Maven / Gradle / Ivy

package com.sdl.webapp.tridion.navigation;

import com.google.common.base.Strings;
import com.sdl.dxa.api.datamodel.model.SitemapItemModelData;
import com.sdl.dxa.api.datamodel.model.TaxonomyNodeModelData;
import com.sdl.dxa.common.dto.DepthCounter;
import com.sdl.dxa.common.dto.SitemapRequestDto;
import com.sdl.dxa.common.util.PathUtils;
import com.sdl.dxa.performance.Performance;
import com.sdl.dxa.tridion.navigation.dynamic.NavigationModelProvider;
import com.sdl.dxa.tridion.navigation.dynamic.OnDemandNavigationModelProvider;
import com.sdl.webapp.common.api.content.LinkResolver;
import com.sdl.webapp.common.api.localization.Localization;
import com.sdl.webapp.common.api.model.entity.NavigationLinks;
import com.sdl.webapp.common.api.model.entity.SitemapItem;
import com.sdl.webapp.common.api.model.entity.TaxonomyNode;
import com.sdl.webapp.common.api.navigation.NavigationFilter;
import com.sdl.webapp.common.api.navigation.NavigationProvider;
import com.sdl.webapp.common.api.navigation.NavigationProviderException;
import com.sdl.webapp.common.api.navigation.OnDemandNavigationProvider;
import com.sdl.webapp.common.exceptions.DxaItemNotFoundException;
import com.sdl.webapp.tridion.fields.exceptions.TaxonomyNotFoundException;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.google.common.base.Strings.isNullOrEmpty;
import static com.sdl.dxa.common.util.PathUtils.isHomePath;

/**
 * Navigation Provider implementation based on Taxonomies (Categories & Keywords).
 * 

Falls back to {@link StaticNavigationProvider} when dynamic navigation is not available.

*/ @Slf4j @Service @Primary @Profile("dynamic.navigation.provider") public class DynamicNavigationProvider implements NavigationProvider, OnDemandNavigationProvider { private final StaticNavigationProvider staticNavigationProvider; private final NavigationModelProvider navigationModelProvider; private final OnDemandNavigationModelProvider onDemandNavigationModelProvider; private final LinkResolver linkResolver; @Autowired public DynamicNavigationProvider(StaticNavigationProvider staticNavigationProvider, LinkResolver linkResolver, NavigationModelProvider navigationModelProvider, OnDemandNavigationModelProvider onDemandNavigationModelProvider) { this.staticNavigationProvider = staticNavigationProvider; this.linkResolver = linkResolver; this.navigationModelProvider = navigationModelProvider; this.onDemandNavigationModelProvider = onDemandNavigationModelProvider; } @Override public SitemapItem getNavigationModel(Localization localization) throws NavigationProviderException { try(Performance perf = new Performance(1_000L, "getNavigationModel")) { Optional navigationModel = getNavigationModelInternal(localization); if (!navigationModel.isPresent()) { return staticNavigationProvider.getNavigationModel(localization); } return convert(navigationModel.get()); } } @Override public NavigationLinks getTopNavigationLinks(String requestPath, Localization localization) throws NavigationProviderException { try(Performance perf = new Performance(1_000L, "getTopNavigationLinks")) { Optional navigationModel = getNavigationModelInternal(localization); if (!navigationModel.isPresent()) { return staticNavigationProvider.getTopNavigationLinks(requestPath, localization); } return toNavigationLinksInternal(navigationModel.get().getItems(), true, localization); } } @Override public NavigationLinks getContextNavigationLinks(String requestPath, Localization localization) throws NavigationProviderException { try(Performance perf = new Performance(1_000L, "getContextNavigationLinks")) { Optional navigationModel = getNavigationModelInternal(localization); if (!navigationModel.isPresent()) { return staticNavigationProvider.getContextNavigationLinks(requestPath, localization); } navigationModel.get().rebuildParentRelationships(); SitemapItemModelData currentLevel = navigationModel.get().findWithUrl(PathUtils.stripDefaultExtension(requestPath)); if (currentLevel != null && !(currentLevel instanceof TaxonomyNodeModelData)) { currentLevel = currentLevel.getParent(); } return toNavigationLinksInternal(currentLevel == null ? Collections.emptySet() : currentLevel.getItems(), true, localization); } } @Override public NavigationLinks getBreadcrumbNavigationLinks(String requestPath, Localization localization) throws NavigationProviderException { try(Performance perf = new Performance(1_000L, "getBreadcrumbNavigationLinks")) { Optional navigationModel = getNavigationModelInternal(localization); if (!navigationModel.isPresent()) { return staticNavigationProvider.getBreadcrumbNavigationLinks(requestPath, localization); } navigationModel.get().rebuildParentRelationships(); SitemapItemModelData currentLevel = navigationModel.get().findWithUrl(PathUtils.stripDefaultExtension(requestPath)); Collection items = currentLevel == null ? Collections.emptySet() : collectBreadcrumbsToLevel(currentLevel, localization); return toNavigationLinksInternal(items,false, localization); } } @Override public Collection getNavigationSubtree(@Nullable String sitemapItemId, @NonNull NavigationFilter navigationFilter, @NonNull Localization localization) throws DxaItemNotFoundException { try(Performance perf = new Performance(1_000L, "getNavigationSubtree")) { Optional> subtree; SitemapRequestDto requestDto = SitemapRequestDto .builder(Integer.parseInt(localization.getId())) .navigationFilter(navigationFilter) .expandLevels(new DepthCounter(navigationFilter.getDescendantLevels())) .sitemapId(sitemapItemId) .uriType(localization.getCmUriScheme()) .build(); subtree = onDemandNavigationModelProvider.getNavigationSubtree(requestDto); if (!subtree.isPresent()) { throw new TaxonomyNotFoundException("Keyword '" + requestDto.getSitemapId() + "' in publication '" + requestDto.getLocalizationId() + "' was not found."); } return subtree.get().stream() .map(this::convert) .collect(Collectors.toList()); } } @NotNull private NavigationLinks toNavigationLinksInternal(Collection items, boolean onlyVisible, Localization localization) { return items.stream() .filter(model -> !onlyVisible || (model.isVisible() && !isNullOrEmpty(model.getUrl()))) .map(this::convert) .map(item -> item.createLink(linkResolver, localization)) .collect(Collectors.collectingAndThen(Collectors.toList(), NavigationLinks::new)); } @NotNull private List collectBreadcrumbsToLevel(SitemapItemModelData currentLevel, final Localization localization) { List breadcrumbs = new LinkedList<>(); SitemapItemModelData model = currentLevel; boolean hasHome = false; while (model.getParent() != null) { breadcrumbs.add(model); hasHome = isHomePath(model.getUrl(), localization.getPath()); model = model.getParent(); } // The Home TaxonomyNode/Keyword may be a top-level sibling instead of an ancestor if (!hasHome) { SitemapItemModelData item = model.getItems().stream() .filter(input -> isHomePath(input.getUrl(), localization.getPath())) .findFirst() .orElse(null); if (item != null) { breadcrumbs.add(item); } } Collections.reverse(breadcrumbs); return breadcrumbs; } @NotNull private Optional getNavigationModelInternal(Localization localization) { SitemapRequestDto requestDto = SitemapRequestDto.wholeTree(Integer.parseInt(localization.getId())).build(); Optional 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()); } @NotNull private SitemapItem convert(@NotNull SitemapItemModelData model) { SitemapItem item = instantiateSitemap(model); item.setId(model.getId()); item.setVisible(model.isVisible()); if (!Strings.isNullOrEmpty(model.getUrl())) item.setUrl("/" + model.getUrl().replaceAll("^/?(.*)", "$1")); item.setTitle(model.getTitle()); item.setOriginalTitle(model.getOriginalTitle()); item.setPublishedDate(model.getPublishedDate()); item.setType(model.getType()); model.getItems().forEach(modelData -> item.addItem(convert(modelData))); return item; } @NotNull private SitemapItem instantiateSitemap(@NotNull SitemapItemModelData model) { if (model instanceof TaxonomyNodeModelData) { TaxonomyNodeModelData taxonomyModel = (TaxonomyNodeModelData) model; TaxonomyNode item = new TaxonomyNode(); item.setKey(taxonomyModel.getKey()); item.setClassifiedItemsCount(taxonomyModel.getClassifiedItemsCount()); item.setDescription(taxonomyModel.getDescription()); item.setTaxonomyAbstract(taxonomyModel.isTaxonomyAbstract()); item.setWithChildren(taxonomyModel.isWithChildren()); return item; } else { return new SitemapItem(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy