Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Issue #2526] Lazy evaluate navigation sub-items on demand #2527

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
import com.day.cq.wcm.api.designer.Style;

@Model(adaptables = SlingHttpServletRequest.class,
adapters = {Breadcrumb.class, ComponentExporter.class},
resourceType = {BreadcrumbImpl.RESOURCE_TYPE_V1, BreadcrumbImpl.RESOURCE_TYPE_V2})
adapters = {Breadcrumb.class, ComponentExporter.class},
resourceType = {BreadcrumbImpl.RESOURCE_TYPE_V1, BreadcrumbImpl.RESOURCE_TYPE_V2})
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class BreadcrumbImpl extends AbstractComponentImpl implements Breadcrumb {

Expand All @@ -62,9 +62,6 @@ public class BreadcrumbImpl extends AbstractComponentImpl implements Breadcrumb
@ScriptVariable
private Page currentPage;

@Self
private SlingHttpServletRequest request;

@Self
protected LinkManager linkManager;

Expand All @@ -88,12 +85,6 @@ public Collection<NavigationItem> getItems() {
return Collections.unmodifiableList(items);
}

@NotNull
@Override
public String getExportedType() {
return request.getResource().getResourceType();
}

private List<NavigationItem> createItems() {
List<NavigationItem> items = new ArrayList<>();
int currentLevel = currentPage.getDepth();
Expand All @@ -105,8 +96,7 @@ private List<NavigationItem> createItems() {
break;
}
if (checkIfNotHidden(page)) {
NavigationItem navigationItem = newBreadcrumbItem(page, isActivePage, linkManager, currentLevel,
Collections.emptyList(), getId(), component);
NavigationItem navigationItem = newBreadcrumbItem(page, isActivePage, linkManager, currentLevel, getId(), component);
items.add(navigationItem);
}
}
Expand All @@ -115,11 +105,37 @@ private List<NavigationItem> createItems() {
return items;
}

protected NavigationItem newBreadcrumbItem(Page page, boolean active, @NotNull LinkManager linkManager, int level, List<NavigationItem> children, String parentId, Component component) {
return new BreadcrumbItemImpl(page, active, linkManager, level, children, parentId, component);
/**
* Create a Breadcrumb Item.
*
* @param page The page for which to create a breadcrumb item.
* @param active Flag indicating if the breadcrumb item is active.
* @param linkManager Link manager service.
* @param level Depth level of the navigation item.
* @param parentId ID of the parent navigation component.
* @param component The parent navigation {@link Component}.
*/
protected NavigationItem newBreadcrumbItem(@NotNull final Page page,
final boolean active,
@NotNull final LinkManager linkManager,
final int level,
final String parentId,
final Component component) {
return new BreadcrumbItemImpl(page, active, linkManager, level, parentId, component);
}

private boolean checkIfNotHidden(Page page) {
return !page.isHideInNav() || showHidden;
/**
* Check if a page should be shown in the breadcrumb
* A page should be shown if either
* <ul>
* <li>`showHidden` is set to true for this component; or,</li>
* <li>The page is not configured to be hidden in navigation</li>
* </ul>
*
* @param page The page to check.
* @return True if the page should be shown in the breadcrumb, false if not.
*/
private boolean checkIfNotHidden(@NotNull final Page page) {
return this.showHidden || !page.isHideInNav();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package com.adobe.cq.wcm.core.components.internal.models.v1;

import java.util.List;
import java.util.Collections;

import org.jetbrains.annotations.NotNull;

Expand All @@ -28,12 +28,29 @@

import static org.apache.sling.api.SlingConstants.PROPERTY_PATH;

@JsonIgnoreProperties(value = {"page", "children", "level", "description", "lastModified",PROPERTY_PATH})
/**
* V1 Breadcrumb Item Implementation.
*/
@JsonIgnoreProperties(value = {"page", "children", "level", "description", "lastModified", PROPERTY_PATH})
public class BreadcrumbItemImpl extends NavigationItemImpl implements NavigationItem {

public BreadcrumbItemImpl(Page page, boolean active, @NotNull LinkManager linkManager, int level,
List<NavigationItem> children, String parentId, Component component) {
super(page, active, active, linkManager, level, children, parentId, component);
/**
* Construct a Breadcrumb Item.
*
* @param page The page for which to create a breadcrumb item.
* @param active Flag indicating if the breadcrumb item is active.
* @param linkManager Link manager service.
* @param level Depth level of the navigation item.
* @param parentId ID of the parent navigation component.
* @param component The parent navigation {@link Component}.
*/
public BreadcrumbItemImpl(@NotNull final Page page,
final boolean active,
@NotNull final LinkManager linkManager,
final int level,
final String parentId,
final Component component) {
super(page, active, active, linkManager, level, Collections::emptyList, parentId, component);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

import javax.annotation.PostConstruct;

Expand All @@ -45,18 +47,25 @@
import com.day.cq.wcm.api.components.Component;
import com.day.cq.wcm.api.designer.Style;

/**
* V1 Language Navigation model implementation.
*/
@Model(adaptables = SlingHttpServletRequest.class,
adapters = {LanguageNavigation.class, ComponentExporter.class},
resourceType = {LanguageNavigationImpl.RESOURCE_TYPE})
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME ,
extensions = ExporterConstants.SLING_MODEL_EXTENSION)
adapters = {LanguageNavigation.class, ComponentExporter.class},
resourceType = {LanguageNavigationImpl.RESOURCE_TYPE})
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class LanguageNavigationImpl extends AbstractComponentImpl implements LanguageNavigation {

/**
* V2 Language Navigation resource type.
*/
public static final String RESOURCE_TYPE = "core/wcm/components/languagenavigation/v1/languagenavigation";
private static final String PN_ACCESSIBILITY_LABEL = "accessibilityLabel";

@Self
private SlingHttpServletRequest request;
/**
* Property name for the accessibility lavel.
*/
private static final String PN_ACCESSIBILITY_LABEL = "accessibilityLabel";

@ScriptVariable
private Page currentPage;
Expand All @@ -73,23 +82,23 @@ public class LanguageNavigationImpl extends AbstractComponentImpl implements Lan
@Nullable
private String accessibilityLabel;

@Nullable
private String navigationRoot;
private int structureDepth;
private Page rootPage;
private List<NavigationItem> items;
private int startLevel;

@PostConstruct
private void initModel() {
navigationRoot = properties.get(PN_NAVIGATION_ROOT, currentStyle.get(PN_NAVIGATION_ROOT, String.class));
navigationRoot = Optional.ofNullable(properties.get(PN_NAVIGATION_ROOT, String.class))
.orElseGet(() -> currentStyle.get(PN_NAVIGATION_ROOT, String.class));
structureDepth = properties.get(PN_STRUCTURE_DEPTH, currentStyle.get(PN_STRUCTURE_DEPTH, 1));
}

@Override
public List<NavigationItem> getItems() {
if (items == null) {
PageManager pageManager = currentPage.getPageManager();
rootPage = pageManager.getPage(navigationRoot);
Page rootPage = Optional.ofNullable(this.navigationRoot).map(currentPage.getPageManager()::getPage).orElse(null);
if (rootPage != null) {
int rootPageLevel = rootPage.getDepth();
startLevel = rootPageLevel + 1;
Expand All @@ -111,44 +120,64 @@ public String getAccessibilityLabel() {
return this.accessibilityLabel;
}

@NotNull
@Override
public String getExportedType() {
return request.getResource().getResourceType();
}

private List<NavigationItem> getItems(Page root) {
/**
* Get localized navigation items under a given page.
*
* @param root The page.
* @return Localized navigation items based on the children of `Page`.
*/
private List<NavigationItem> getItems(@NotNull final Page root) {
List<NavigationItem> pages = new ArrayList<>();
if (root.getDepth() < structureDepth) {
Iterator<Page> it = root.listChildren(new PageFilter());
while (it.hasNext()) {
Page page = it.next();
boolean active = currentPage.getPath().equals(page.getPath()) || currentPage.getPath().startsWith(page.getPath() + "/");
String title = page.getNavigationTitle();
if (title == null) {
title = page.getTitle();
}
List<NavigationItem> children = getItems(page);
final Page page = it.next();
String title = PageListItemImpl.getTitle(page);
int level = page.getDepth() - startLevel;
Page localizedPage = getLocalizedPage(currentPage, page);
if (localizedPage != null) {
page = localizedPage;
}
boolean current = currentPage.getPath().equals(page.getPath());
pages.add(newLanguageNavigationItem(page, active, current, linkManager, level, children, title, getId(), component));
Page targetPage = Optional.ofNullable(getLocalizedPage(currentPage, page)).orElse(page);
boolean current = currentPage.getPath().equals(targetPage.getPath());
boolean active = currentPage.getPath().equals(page.getPath()) || currentPage.getPath().startsWith(page.getPath() + "/");
pages.add(newLanguageNavigationItem(targetPage, active, current, linkManager, level, () -> getItems(page), title, getId(), component));
}
}

return pages;
}

protected LanguageNavigationItem newLanguageNavigationItem(Page page, boolean active, boolean current, @NotNull LinkManager linkManager,
int level, List<NavigationItem> children, String title, String parentId,
Component component) {
return new LanguageNavigationItemImpl(page, active, current, linkManager, level, children, title, parentId, component);
/**
* Construct a Navigation Item.
*
* @param page The page for which to create a navigation item.
* @param active Flag indicating if the navigation item is active.
* @param current Flag indicating if the navigation item is current page.
* @param linkManager Link manager service.
* @param level Depth level of the navigation item.
* @param childrenSupplier The child navigation items supplier.
* @param title The item title.
* @param parentId ID of the parent navigation component.
* @param component The parent navigation {@link Component}.
*/
protected LanguageNavigationItem newLanguageNavigationItem(@NotNull final Page page,
final boolean active,
final boolean current,
@NotNull final LinkManager linkManager,
final int level,
@NotNull final Supplier<List<NavigationItem>> childrenSupplier,
final String title,
final String parentId,
final Component component) {
return new LanguageNavigationItemImpl(page, active, current, linkManager, level, childrenSupplier, title, parentId, component);
}

private Page getLocalizedPage(Page page, Page languageRoot) {
/**
* Get the localized version of page found under the `languageRoot`.
*
* @param page The page for which to get the localized page.
* @param languageRoot The language root page for the locale under which to find the localized page.
* @return The localized page under languageRoot, or null if no such page exists.
*/
@Nullable
private Page getLocalizedPage(@NotNull final Page page, @NotNull final Page languageRoot) {
Page localizedPage;
String path = languageRoot.getPath();
String relativePath = page.getPath();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.List;
import java.util.Locale;
import java.util.function.Supplier;

import org.jetbrains.annotations.NotNull;

Expand All @@ -28,21 +29,61 @@
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.components.Component;

/**
* V1 Language Navigation Item implementation.
*/
public class LanguageNavigationItemImpl extends NavigationItemImpl implements LanguageNavigationItem {

protected String title;
protected Locale locale;
protected String country;
protected String language;
/**
* Navigation item title.
*/
private final String title;

public LanguageNavigationItemImpl(Page page, boolean active, boolean current, @NotNull LinkManager linkManager, int level,
List<NavigationItem> children, String title, String parentId, Component component) {
super(page, active, current, linkManager, level, children, parentId, component);
/**
* Locale for this language navigation item.
*/
private Locale locale;

/**
* Country for this language navigation item.
*/
private String country;

/**
* Language for this language navigation item.
*/
private String language;

/**
* Construct a Language Navigation Item.
*
* @param page The page for which to create a navigation item.
* @param active Flag indicating if the navigation item is active.
* @param current Flag indicating if the navigation item is current page.
* @param linkManager Link manager service.
* @param level Depth level of the navigation item.
* @param childrenSupplier The child navigation items supplier.
* @param title The item title.
* @param parentId ID of the parent navigation component.
* @param component The parent navigation {@link Component}.
*/
public LanguageNavigationItemImpl(@NotNull final Page page,
final boolean active,
final boolean current,
@NotNull final LinkManager linkManager,
final int level,
@NotNull final Supplier<List<NavigationItem>> childrenSupplier,
final String title,
final String parentId,
final Component component) {
super(page, active, current, linkManager, level, childrenSupplier, parentId, component);
this.title = title;
}

@Override
public String getTitle() { return title; }
public String getTitle() {
return title;
}

@Override
public Locale getLocale() {
Expand Down