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

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

package com.sdl.webapp.tridion.navigation;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sdl.dxa.common.dto.PageRequestDto;
import com.sdl.dxa.common.util.PathUtils;
import com.sdl.dxa.modelservice.service.ModelServiceProvider;
import com.sdl.webapp.common.api.content.ContentProviderException;
import com.sdl.webapp.common.api.content.LinkResolver;
import com.sdl.webapp.common.api.localization.Localization;
import com.sdl.webapp.common.api.model.entity.Link;
import com.sdl.webapp.common.api.model.entity.NavigationLinks;
import com.sdl.webapp.common.api.model.entity.SitemapItem;
import com.sdl.webapp.common.api.navigation.NavigationProvider;
import com.sdl.webapp.common.api.navigation.NavigationProviderException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * Implementation of {@link NavigationProvider} based on statically generated (published) Navigation.json.
 */
@Slf4j
@Service
public class StaticNavigationProvider implements NavigationProvider {

    static final String TYPE_STRUCTURE_GROUP = "StructureGroup";

    private final ObjectMapper objectMapper;

    private final LinkResolver linkResolver;

    private final ModelServiceProvider modelService;

    @Value("${dxa.tridion.navigation.modelUrl}")
    private String navigationModelUrl;

    @Autowired
    public StaticNavigationProvider(ObjectMapper objectMapper,
                                    LinkResolver linkResolver,
                                    ModelServiceProvider modelService) {
        this.objectMapper = objectMapper;
        this.linkResolver = linkResolver;
        this.modelService = modelService;
    }

    private static List createLinksForVisibleItems(Iterable items) {
        final List links = new ArrayList<>();
        for (SitemapItem item : items) {
            if (item.isVisible()) {
                links.add(linkForItem(item));
            }
        }
        return links;
    }

    private static SitemapItem findContextNavigationStructureGroup(SitemapItem item, String requestPath) {
        if (Objects.equals(item.getType(), TYPE_STRUCTURE_GROUP) && requestPath.startsWith(item.getUrl().toLowerCase())) {
            // Check if there is a matching subitem, if yes, then return it
            for (SitemapItem subItem : item.getItems()) {
                final SitemapItem matchingSubItem = findContextNavigationStructureGroup(subItem, requestPath);
                if (matchingSubItem != null) {
                    return matchingSubItem;
                }
            }

            // Otherwise return this matching item
            return item;
        }

        // No matching item
        return null;
    }

    private static boolean createBreadcrumbLinks(SitemapItem item, String requestPath, List links) {
        if (requestPath.equalsIgnoreCase(item.getUrl())) {
            // This item matches. Add it and exit this function.
            links.add(linkForItem(item));
            return true;
        }

        for (SitemapItem subItem : item.getItems()) {
            if (createBreadcrumbLinks(subItem, requestPath, links)) {
                // A child of the item matches, add this item as the path to it.
                links.add(linkForItem(item));
                return true;
            }
        }

        // Nothing matched
        return false;
    }

    private static Link linkForItem(SitemapItem item) {
        Link link = new Link();
        link.setUrl(item.getUrl());
        link.setLinkText(item.getTitle());
        return link;
    }

    @Override
    public SitemapItem getNavigationModel(Localization localization) throws NavigationProviderException {
        try {
            final String path = localization.localizePath(navigationModelUrl);

            return resolveLinks(objectMapper.readValue(getPageContent(path, localization), SitemapItem.class), localization);
        } catch (ContentProviderException | IOException e) {
            throw new NavigationProviderException("Exception while loading navigation model", e);
        }
    }

    @Override
    public NavigationLinks getTopNavigationLinks(String requestPath, Localization localization)
            throws NavigationProviderException {
        final SitemapItem navigationModel = getNavigationModel(localization);

        return new NavigationLinks(createLinksForVisibleItems(navigationModel.getItems()));
    }

    @Override
    public NavigationLinks getContextNavigationLinks(String requestPath, Localization localization)
            throws NavigationProviderException {
        final SitemapItem navigationModel = getNavigationModel(localization);
        final SitemapItem contextNavigationItem = findContextNavigationStructureGroup(navigationModel, requestPath);

        final List links = contextNavigationItem != null ? createLinksForVisibleItems(contextNavigationItem.getItems()) : Collections.emptyList();

        return new NavigationLinks(links);
    }

    @Override
    public NavigationLinks getBreadcrumbNavigationLinks(String requestPath, Localization localization)
            throws NavigationProviderException {
        final SitemapItem navigationModel = getNavigationModel(localization);

        final List links = new ArrayList<>();
        requestPath = PathUtils.stripDefaultExtension(requestPath);
        createBreadcrumbLinks(navigationModel, requestPath, links);
        Collections.reverse(links);

        return new NavigationLinks(links);
    }

    private SitemapItem resolveLinks(SitemapItem sitemapItem, Localization localization) {
        sitemapItem.setUrl(linkResolver.resolveLink(sitemapItem.getUrl(), localization.getId()));

        for (SitemapItem subItem : sitemapItem.getItems()) {
            resolveLinks(subItem, localization);
        }

        return sitemapItem;
    }

    protected InputStream getPageContent(String path, Localization localization) throws ContentProviderException {
        String pageContent = modelService.loadPageContent(PageRequestDto.builder(localization.getId(), path).build());
        // NOTE: This assumes page content is always in UTF-8 encoding
        return new ByteArrayInputStream(pageContent.getBytes(StandardCharsets.UTF_8));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy