Goal
AEM Cloud Version : 2021.3.5087.20210322T071003Z-210225 (March 22, 2021)
The default sling model exporter com.adobe.aem.spa.project.core.internal.impl.PageImpl for page resource types with super type spa-project-core/components/page does not add custom or additional properties on the page content node like vanity path to the response. You can always create a project specific model implementation for your pages extending com.adobe.aem.spa.project.core.models.Page, however for adding just few additional properties a simple filter might be an easier solution...
Solution
Add a filter apps.experienceaem.sites.core.filters.EAEMExtendPageModelFilter with filter pattern sling.filter.pattern=.*.model.json to intercept model requests, capture the response and add additional properties (eg. sling:vanityPath) to the response reading it from cq:PageContent node
package apps.experienceaem.sites.core.filters; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.api.wrappers.SlingHttpServletResponseWrapper; import org.apache.sling.commons.json.JSONObject; import org.osgi.framework.Constants; import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.*; import java.io.CharArrayWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.Iterator; @Component( service = Filter.class, immediate = true, name = "Expereince AEM Sling Model Response Modifier Servlet Filter", property = { Constants.SERVICE_RANKING + ":Integer=-99", "sling.filter.scope=COMPONENT", "sling.filter.pattern=.*.model.json" } ) public class EAEMExtendPageModelFilter implements Filter { private static Logger log = LoggerFactory.getLogger(EAEMExtendPageModelFilter.class); private static final String SLING_VANITYPATH = "sling:vanityPath"; private static final String SLING_VANITYPATH_JSON_PROP = "eaemVanityPath"; private static final String CHILDREN = ":children"; @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try{ SlingHttpServletRequest slingRequest = (SlingHttpServletRequest)request; String uri = slingRequest.getRequestURI(); if(!uri.endsWith(".model.json")){ chain.doFilter(request, response); return; } SlingHttpServletResponse modelResponse = getModelResponse((SlingHttpServletResponse) response); chain.doFilter(slingRequest, modelResponse); PrintWriter responseWriter = response.getWriter(); responseWriter.write(getModifiedContent(modelResponse.toString(), slingRequest)); }catch(ServletException e){ throw new ServletException("Error at EAEMExtendPageModelFilter.this.doFilter()"); } } SlingHttpServletResponse getModelResponse(SlingHttpServletResponse response) { SlingHttpServletResponse modelResponse = new DefaultSlingModelResponseWrapper(response); return modelResponse; } private String getModifiedContent(String origContent, SlingHttpServletRequest slingRequest){ String modifiedContent = origContent; try{ JSONObject model = new JSONObject(origContent); addAddnPropertiesInPageModel(model, slingRequest); modifiedContent = model.toString(); }catch(Exception e){ log.error("Error at EAEMExtendPageModelFilter.this.getModifiedContent(origContent={}) {}", origContent, e); modifiedContent = origContent; } return modifiedContent; } private void addAddnPropertiesInPageModel(JSONObject model, SlingHttpServletRequest slingRequest) throws Exception{ if(!model.has(CHILDREN)){ return; } JSONObject childrenModel = model.getJSONObject(CHILDREN); Iterator<String> childrenItr = childrenModel.keys(); ResourceResolver resolver = slingRequest.getResourceResolver(); Resource pageContent; while(childrenItr.hasNext()) { String key = childrenItr.next(); JSONObject childData = childrenModel.getJSONObject(key); pageContent = resolver.getResource(key + "/jcr:content"); if(pageContent == null){ continue; } ValueMap vm = pageContent.getValueMap(); String slingVanityPath = vm.get(SLING_VANITYPATH, ""); if(StringUtils.isNotEmpty(slingVanityPath)){ childData.put(SLING_VANITYPATH_JSON_PROP, slingVanityPath); } } } @Override public void destroy() { } private class DefaultSlingModelResponseWrapper extends SlingHttpServletResponseWrapper { private CharArrayWriter writer; public DefaultSlingModelResponseWrapper (final SlingHttpServletResponse response) { super(response); writer = new CharArrayWriter(); } public PrintWriter getWriter() throws IOException { return new PrintWriter(writer); } public String toString() { return writer.toString(); } } }
Just one update:
ReplyDeleteuse Gson object in place of JSONObject, its deprecated.
Example:
Gson gson = new Gson();
JsonObject enJsonObj = gson.fromJson(origEnJsonContent, JsonObject.class);
JsonObject externalEndPoints = new JsonObject();