Extend Experience fragments of core using delegation pattern | Community
Skip to main content
Level 3
December 19, 2023
Solved

Extend Experience fragments of core using delegation pattern

  • December 19, 2023
  • 2 replies
  • 2456 views

Hello Members,

 

We are trying to extend Core Experience fragment to meet our client needs where each page can have its own header and footer.

The approach we are trying now is to unlock the experience fragment in the structure so that the author can configure per page which works fine.

 

However, on top of this we need same Experience fragment header to be set for all the child pages below . I am using Delegate pattern for the same and unfortunately this seems to not work .

 

Note: We are using aem-react-editable-components so the mapping happens in import-component.js and not the usual way with html. 

 

 

Below is my Implementation

 

@Model(adaptables = { Resource.class, SlingHttpServletRequest.class }, adapters = { ExperienceFragment.class }, resourceType = CustomExperienceFragmentImpl.RESOURCE_TYPE, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) @Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION) @Getter public class CustomExperienceFragmentImpl implements ExperienceFragment { protected static final String RESOURCE_TYPE = "myproject/components/core/experiencefragment"; @Self @Getter(AccessLevel.NONE) private SlingHttpServletRequest request; private String fragmentVariationPath; @PostConstruct void init() { InheritanceValueMap properties = new HierarchyNodeInheritanceValueMap(request.getResource()); fragmentVariationPath = properties.getInherited(ExperienceFragment.PN_FRAGMENT_VARIATION_PATH, String.class); } @Self // Indicates that we are resolving the current resource @Getter(AccessLevel.NONE) @Via(type = ResourceSuperType.class) // Resolve not as this model, but as the model of our supertype (ie: CC Image) @Delegate(excludes = DelegationExclusion.class) // Delegate all our methods to the CC Image except those defined below private ExperienceFragment delegate; @Override public String getLocalizedFragmentVariationPath() { return fragmentVariationPath!=null ? fragmentVariationPath : delegate.getLocalizedFragmentVariationPath(); } private interface DelegationExclusion { String getLocalizedFragmentVariationPath(); // Override the method which determines the source of the asset } }

 

Kindly share your views

 

Thanks

Arun

This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.
Best answer by Jeevan-Eranti

Hi @user70744 

 

Please refer to the article below for instructions on configuring dynamic headers and footers. Additionally, explore extending the Experience Fragment model to support localized headers/footers with a custom site structure using the delegation pattern.

https://www.albinsblog.com/2021/10/extend-aem-experience-fragment-localization.html

 

2 replies

Jeevan-ErantiAccepted solution
Level 2
December 19, 2023

Hi @user70744 

 

Please refer to the article below for instructions on configuring dynamic headers and footers. Additionally, explore extending the Experience Fragment model to support localized headers/footers with a custom site structure using the delegation pattern.

https://www.albinsblog.com/2021/10/extend-aem-experience-fragment-localization.html

 

user70744Author
Level 3
December 19, 2023

hi @jeevan-eranti yes this is the same approach i have implemented unfortunately the delegation does not seem to work for me.

 

The same code is attached as well

 

Thanks

Arun

Level 2
December 19, 2023

@user70744 
We followed the steps below to implement the Proxy Experience Fragment for the header and footer at the template level:

  1. Integrated the Proxy Experience Fragment component into the template structure at the template level. It's important to note that we kept these components locked.

  2. Introduced two custom fields in the Page component dialog to facilitate the configuration of XF paths for the header and footer.

    Note: XF paths for the header and footer can be defined at the site root page level or any child page if a different header or footer is desired. This allows for flexibility in customizing header and footer content at different levels of the site hierarchy.

    Additionally, a logic has been implemented in the Customized Experience Fragment model to handle the inheritance of XF paths for the header and footer.

     

     

  3. Provided a sample code snippet for the Customized Experience Fragment model to manage the inheritance logic and delegation pattern.       

 

package com.test.core.models; import org.apache.sling.models.annotations.Model; import org.apache.sling.api.resource.Resource; import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.models.annotations.DefaultInjectionStrategy; import org.apache.sling.models.annotations.Exporter; import org.apache.sling.models.annotations.injectorspecific.Self; import org.apache.sling.models.annotations.Via; import org.apache.sling.models.annotations.via.ResourceSuperType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import lombok.experimental.Delegate; import com.adobe.cq.export.json.ExporterConstants; import com.adobe.cq.wcm.core.components.models.ExperienceFragment; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.models.annotations.injectorspecific.SlingObject; import org.apache.sling.models.annotations.injectorspecific.ScriptVariable; import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy; import com.day.cq.commons.inherit.HierarchyNodeInheritanceValueMap; import com.day.cq.commons.inherit.InheritanceValueMap; import com.day.cq.wcm.api.Page; import com.day.cq.wcm.api.PageManager; @Model(adaptables = { Resource.class, SlingHttpServletRequest.class }, adapters = ExperienceFragment.class, resourceType = CustomExperienceFragmentImpl.RESOURCE_TYPE, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) @Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION) public class CustomExperienceFragmentImpl implements ExperienceFragment { public static final String RESOURCE_TYPE = "test/components/experiencefragment"; public static final String XF_TEMPLATE = "/conf/test/settings/wcm/templates/xf-web-variation"; @Self @Via(type = ResourceSuperType.class) @Delegate(excludes = DelegationExclusion.class) private ExperienceFragment delegate; @Self private SlingHttpServletRequest request; @SlingObject private ResourceResolver resourceResolver; @ScriptVariable(injectionStrategy = InjectionStrategy.OPTIONAL) private Page currentPage; private static final Logger LOGGER = LoggerFactory.getLogger(CustomExperienceFragmentImpl.class); private static final String XF_HEADER_VARIATION = "/header/"; private static final String XF_FOOTER_VARIATION = "/footer/"; /* * Return the site specific headers and footers, delegate to Core Component * ExperienceFragment Model for remaining scenarios */ @Override public String getLocalizedFragmentVariationPath() { try { if (currentPage != null && currentPage.getAbsoluteParent(2) != null) { String fragmentVariationPath = request.getResource().getValueMap() .get(ExperienceFragment.PN_FRAGMENT_VARIATION_PATH, String.class); if (StringUtils.isNoneEmpty(fragmentVariationPath)) { LOGGER.debug("FragmentVariationPath: {}", fragmentVariationPath); if (fragmentVariationPath.contains(XF_HEADER_VARIATION)) { InheritanceValueMap ivm = new HierarchyNodeInheritanceValueMap( currentPage.getContentResource()); String headeroverrideXF = ivm.getInherited("headerFragmentPath", String.class); if (StringUtils.isNoneEmpty(headeroverrideXF) && resourceExists(headeroverrideXF) && isValidExperienceFragmentVariation(headeroverrideXF)) { LOGGER.debug("Handling the header variation. XfVariationPath - {} ", headeroverrideXF); return headeroverrideXF; } } else if (fragmentVariationPath.contains(XF_FOOTER_VARIATION)) { InheritanceValueMap ivm = new HierarchyNodeInheritanceValueMap( currentPage.getContentResource()); String footeroverrideXF = ivm.getInherited("footerFragmentPath", String.class); if (StringUtils.isNoneEmpty(footeroverrideXF) && resourceExists(footeroverrideXF) && isValidExperienceFragmentVariation(footeroverrideXF)) { LOGGER.debug("Handling the footer variation. XfVariationPath - {} ", footeroverrideXF); return footeroverrideXF; } } } } } catch (Exception e) { LOGGER.error("Exception thrown at getLocalizedFragmentVariationPath - ExperienceFragmentImpl class " + e); } return Objects.nonNull(delegate) ? delegate.getLocalizedFragmentVariationPath() : StringUtils.EMPTY; } private boolean resourceExists(final String path) { return (StringUtils.isNotEmpty(path) && this.request.getResourceResolver().getResource(path) != null); } private boolean isValidExperienceFragmentVariation(final String path) { PageManager pageManager = this.request.getResourceResolver().adaptTo(PageManager.class); if (pageManager != null) { Page xfVariationPage = pageManager.getPage(path); if (xfVariationPage != null) { String xfPageTemplate = xfVariationPage.getTemplate().getPath(); return XF_TEMPLATE.equals(xfPageTemplate); } } return false; } private interface DelegationExclusion { String getLocalizedFragmentVariationPath(); } }

 

 

arunpatidar
Community Advisor
Community Advisor
December 19, 2023
user70744Author
Level 3
December 19, 2023

Thanks @arunpatidar but this post uses slightly. however i would need to do in Model so that its available in model.json and react would pick the path