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

CIF-2827: Placeholder XF in product list component #930

Merged
merged 34 commits into from
Jul 18, 2022
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
404126e
CIF-2827: Placeholder XF in product list component
laurentiumagureanu Jun 15, 2022
84be920
Merge branch 'master' into issue/CIF-2827
laurentiumagureanu Jun 22, 2022
a95ce75
CIF-2827: Add fragments in correct grid positions
laurentiumagureanu Jun 23, 2022
3df4af7
CIF-2827: Fix formatting
laurentiumagureanu Jun 23, 2022
4b2577a
CIF-2827: Fix formatting
laurentiumagureanu Jun 23, 2022
a7e1f98
CIF-2827: Add unit tests
laurentiumagureanu Jun 24, 2022
21f944a
Merge branch 'master' into issue/CIF-2827
laurentiumagureanu Jun 24, 2022
b210f91
Merge branch 'master' into issue/CIF-2827
laurentiumagureanu Jun 24, 2022
71e2698
CIF-2827: Remove unnecessary flags
laurentiumagureanu Jun 27, 2022
e9589f4
Merge branch 'issue/CIF-2827' of https://github.com/adobe/aem-core-ci…
laurentiumagureanu Jun 27, 2022
47fa419
CIF-2827: Fix formatting
laurentiumagureanu Jun 27, 2022
100b8c5
Merge branch 'master' into issue/CIF-2827
laurentiumagureanu Jun 28, 2022
0f6653e
CIF-2827: Add xf in productlist example
laurentiumagureanu Jun 28, 2022
7420518
CIF-2827: ProducList XFs as separate list
laurentiumagureanu Jul 1, 2022
00c71cd
Merge branch 'master' into issue/CIF-2827
laurentiumagureanu Jul 1, 2022
8c3b3ac
CIF-2827: Fix models version and test content
laurentiumagureanu Jul 1, 2022
d43627f
CIF-2827: Fix examples
laurentiumagureanu Jul 1, 2022
969d6b8
Merge branch 'master' into issue/CIF-2827
laurentiumagureanu Jul 4, 2022
b2f0205
CIF-2827: Fix package version
laurentiumagureanu Jul 6, 2022
a9032f8
Merge branch 'master' into issue/CIF-2827
laurentiumagureanu Jul 7, 2022
264fde7
CIF-2827: make CommerceExperienceFragmentsRetriever internal
laurentiumagureanu Jul 8, 2022
4718124
CIF-2827: Fragment style defined in policy
laurentiumagureanu Jul 8, 2022
12232c5
Merge branch 'master' into issue/CIF-2827
laurentiumagureanu Jul 8, 2022
9762fe1
CIF-2827: Add XF styles datasource
laurentiumagureanu Jul 8, 2022
d3b18e8
CIF-2827: Add XFs only when styles defined
laurentiumagureanu Jul 11, 2022
197ee66
CIF-2827: Add page configuration for produclist XF
laurentiumagureanu Jul 12, 2022
e41aeeb
CIF-2827: Fix formatting
laurentiumagureanu Jul 12, 2022
f0821e0
Merge branch 'master' into issue/CIF-2827
laurentiumagureanu Jul 13, 2022
59f55e2
Merge branch 'master' into issue/CIF-2827
buuhuu Jul 13, 2022
309fd23
CIF-2827: Refactoring
laurentiumagureanu Jul 13, 2022
d593c04
CIF-2827: Fix productlist XF rendercondition
laurentiumagureanu Jul 14, 2022
cb4c2e3
CIF-2827: Disable XF editing in productlist
laurentiumagureanu Jul 14, 2022
bd6a171
move style to examples
buuhuu Jul 15, 2022
962f798
Merge branch 'master' into issue/CIF-2827
buuhuu Jul 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ Copyright 2022 Adobe
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package com.adobe.cq.commerce.core.components.internal.models.v1.common;

import org.apache.sling.api.resource.Resource;

import com.adobe.cq.commerce.core.components.models.common.CommerceExperienceFragmentContainer;

public class CommerceExperienceFragmentContainerImpl implements CommerceExperienceFragmentContainer {

private Resource renderResource;
private String cssClassName;

public CommerceExperienceFragmentContainerImpl(Resource renderResource, String cssClassName) {
this.renderResource = renderResource;
this.cssClassName = cssClassName;
}

@Override
public Resource getRenderResource() {
return renderResource;
}

@Override
public String getCssClassName() {
return cssClassName;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,10 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package com.adobe.cq.commerce.core.components.internal.models.v1.experiencefragment;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RangeIterator;
import javax.jcr.Session;
import javax.jcr.Workspace;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;

import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
Expand All @@ -39,30 +29,22 @@
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.adobe.cq.commerce.core.components.client.MagentoGraphqlClient;
import com.adobe.cq.commerce.core.components.models.common.SiteStructure;
import com.adobe.cq.commerce.core.components.models.experiencefragment.CommerceExperienceFragment;
import com.adobe.cq.commerce.core.components.services.experiencefragments.CommerceExperienceFragmentsRetriever;
import com.adobe.cq.commerce.core.components.services.urls.UrlProvider;
import com.day.cq.wcm.api.LanguageManager;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import com.day.cq.wcm.api.WCMException;
import com.day.cq.wcm.msm.api.LiveCopy;
import com.day.cq.wcm.msm.api.LiveRelationship;
import com.day.cq.wcm.msm.api.LiveRelationshipManager;

@Model(
adaptables = SlingHttpServletRequest.class,
adapters = CommerceExperienceFragment.class,
resourceType = CommerceExperienceFragmentImpl.RESOURCE_TYPE)
public class CommerceExperienceFragmentImpl implements CommerceExperienceFragment {

protected static final String RESOURCE_TYPE = "core/cif/components/commerce/experiencefragment/v1/experiencefragment";
private static final Logger LOGGER = LoggerFactory.getLogger(CommerceExperienceFragmentImpl.class);
private static final String XF_ROOT = "/content/experience-fragments/";
public static final String RESOURCE_TYPE = "core/cif/components/commerce/experiencefragment/v1/experiencefragment";

@Self
private SlingHttpServletRequest request;
Expand All @@ -86,10 +68,7 @@ public class CommerceExperienceFragmentImpl implements CommerceExperienceFragmen
private UrlProvider urlProvider;

@OSGiService
private LanguageManager languageManager;

@OSGiService
private LiveRelationshipManager relationshipManager;
private CommerceExperienceFragmentsRetriever fragmentsRetriever;

@Self
private SiteStructure siteNavigation;
Expand All @@ -99,113 +78,21 @@ public class CommerceExperienceFragmentImpl implements CommerceExperienceFragmen

@PostConstruct
private void initModel() {
String query = null;
List<Resource> xfs = null;
if (siteNavigation.isProductPage(currentPage)) {
query = getQueryForProduct();
String sku = urlProvider.getProductIdentifier(request);
xfs = fragmentsRetriever.getExperienceFragmentsForProduct(sku, fragmentLocation, currentPage);
} else if (siteNavigation.isCategoryPage(currentPage)) {
query = getQueryForCategory();
}

if (query == null) {
return;
String categoryUid = urlProvider.getCategoryIdentifier(request);
xfs = fragmentsRetriever.getExperienceFragmentsForCategory(categoryUid, fragmentLocation, currentPage);
}

List<Resource> xfs = findExperienceFragments(query);
if (!xfs.isEmpty()) {
if (xfs != null && !xfs.isEmpty()) {
xfResource = xfs.get(0);
resolveName();
}
}

private String getQueryForProduct() {
// Extract product sku from request URL
String sku = urlProvider.getProductIdentifier(request);

if (StringUtils.isBlank(sku)) {
LOGGER.warn("Cannot find product for current request");
return null;
}

return buildQueryForProduct(sku);
}

private String buildQueryForProduct(String sku) {
// This query is backed up by an index
final String PRODUCT_QUERY_TEMPLATE = "SELECT * FROM [cq:PageContent] as node WHERE ISDESCENDANTNODE('%s') "
+ "AND (node.[" + PN_CQ_PRODUCTS + "] = '%s' OR node.[" + PN_CQ_PRODUCTS + "] LIKE '%s#%%') "
+ "AND node.[" + PN_FRAGMENT_LOCATION + "] ";

String query = String.format(PRODUCT_QUERY_TEMPLATE, getExperienceFragmentsRoot(), sku, sku);
if (fragmentLocation != null) {
query += "= '" + fragmentLocation + "'";
} else {
query += "IS NULL";
}

return query;
}

private String getQueryForCategory() {
// Extract category uid sku from request URL
String categoryUid = urlProvider.getCategoryIdentifier(request);

if (StringUtils.isBlank(categoryUid)) {
LOGGER.warn("Cannot find category for current request");
return null;
}

return buildQueryForCategory(categoryUid);
}

private String buildQueryForCategory(String categoryId) {
final String CATEGORY_QUERY_TEMPLATE = "SELECT * FROM [cq:PageContent] as node WHERE ISDESCENDANTNODE('%s') "
+ "AND node.[" + PN_CQ_CATEGORIES + "] = '%s' "
+ "AND node.[" + PN_FRAGMENT_LOCATION + "] ";

String query = String.format(CATEGORY_QUERY_TEMPLATE, getExperienceFragmentsRoot(), categoryId);
if (fragmentLocation != null) {
query += "= '" + fragmentLocation + "'";
} else {
query += "IS NULL";
}

return query;
}

private String getExperienceFragmentsRoot() {
String localizationRoot = getLocalizationRoot(currentPage.getPath());
return localizationRoot != null ? localizationRoot.replace("/content/", XF_ROOT) : XF_ROOT;
}

private List<Resource> findExperienceFragments(String query) {
LOGGER.debug("Looking for experience fragments with query: {}", query);

List<Resource> experienceFragments = new ArrayList<>();
try {
Session session = resolver.adaptTo(Session.class);
Workspace workspace = session.getWorkspace();
QueryManager qm = workspace.getQueryManager();
Query jcrQuery = qm.createQuery(query, "JCR-SQL2");
QueryResult result = jcrQuery.execute();
NodeIterator nodes = result.getNodes();
while (nodes.hasNext()) {
Node node = nodes.nextNode();
Resource resource = resolver.getResource(node.getPath());
if (resource != null) {
experienceFragments.add(resource);
}
}
} catch (Exception e) {
LOGGER.error("Error looking for experience fragments", e);
}

if (experienceFragments.size() > 1) {
LOGGER.warn("Found multiple experience fragments matching {} with location {}", request.getRequestURI(), fragmentLocation);
}

return experienceFragments;
}

private void resolveName() {
PageManager pageManager = resolver.adaptTo(PageManager.class);
Page xfVariationPage = pageManager.getPage(xfResource.getParent().getPath());
Expand All @@ -231,91 +118,4 @@ public String getName() {
public String getExportedType() {
return resource.getResourceType();
}

// All the methods below are copied from the WCM ExperienceFragmentImpl class
// and will be OSGi-exported in a new public class in a next release

/**
* Returns the localization root of the resource defined at the given path.
*
* @param path the resource path
* @return the localization root of the resource at the given path if it exists, {@code null} otherwise
*/
private String getLocalizationRoot(String path) {
String root = null;
if (StringUtils.isNotEmpty(path)) {
Resource resource = resolver.getResource(path);
root = getLanguageRoot(resource);
if (StringUtils.isEmpty(root)) {
root = getBlueprintPath(resource);
}
if (StringUtils.isEmpty(root)) {
root = getLiveCopyPath(resource);
}
}
return root;
}

/**
* Returns the language root of the resource.
*
* @param resource the resource
* @return the language root of the resource if it exists, {@code null} otherwise
*/
private String getLanguageRoot(Resource resource) {
Page rootPage = languageManager.getLanguageRoot(resource);
if (rootPage != null) {
return rootPage.getPath();
}
return null;
}

/**
* Returns the path of the blueprint of the resource.
*
* @param resource the resource
* @return the path of the blueprint of the resource if it exists, {@code null} otherwise
*/
private String getBlueprintPath(Resource resource) {
try {
if (relationshipManager.isSource(resource)) {
// the resource is a blueprint
RangeIterator liveCopiesIterator = relationshipManager.getLiveRelationships(resource, null, null);
if (liveCopiesIterator != null) {
LiveRelationship relationship = (LiveRelationship) liveCopiesIterator.next();
LiveCopy liveCopy = relationship.getLiveCopy();
if (liveCopy != null) {
return liveCopy.getBlueprintPath();
}
}
}
} catch (WCMException e) {
LOGGER.error("Unable to get the blueprint: {}", e.getMessage());
}
return null;
}

/**
* Returns the path of the live copy of the resource.
*
* @param resource the resource
* @return the path of the live copy of the resource if it exists, {@code null} otherwise
*/
private String getLiveCopyPath(Resource resource) {
try {
if (relationshipManager.hasLiveRelationship(resource)) {
// the resource is a live copy
LiveRelationship liveRelationship = relationshipManager.getLiveRelationship(resource, false);
if (liveRelationship != null) {
LiveCopy liveCopy = liveRelationship.getLiveCopy();
if (liveCopy != null) {
return liveCopy.getPath();
}
}
}
} catch (WCMException e) {
LOGGER.error("Unable to get the live copy: {}", e.getMessage());
}
return null;
}
}
Loading