TargetHelperServiceImpl.java 16 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  com.adobe.granite.crypto.CryptoException
 *  com.adobe.granite.crypto.CryptoSupport
 *  com.day.cq.commons.Filter
 *  com.day.cq.commons.jcr.JcrUtil
 *  com.day.cq.personalization.Location
 *  com.day.cq.personalization.TargetedContentManager
 *  com.day.cq.wcm.api.Page
 *  com.day.cq.wcm.api.PageManager
 *  com.day.cq.wcm.webservicesupport.Configuration
 *  com.google.common.collect.ImmutableList
 *  javax.jcr.Node
 *  javax.jcr.RepositoryException
 *  javax.jcr.Session
 *  org.apache.commons.lang.StringUtils
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.sling.api.resource.AbstractResourceVisitor
 *  org.apache.sling.api.resource.PersistenceException
 *  org.apache.sling.api.resource.Resource
 *  org.apache.sling.api.resource.ResourceResolver
 *  org.apache.sling.api.resource.ValueMap
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.day.cq.analytics.testandtarget.impl;

import com.adobe.granite.crypto.CryptoException;
import com.adobe.granite.crypto.CryptoSupport;
import com.day.cq.analytics.testandtarget.TestandtargetException;
import com.day.cq.analytics.testandtarget.impl.TargetHelperService;
import com.day.cq.analytics.testandtarget.util.CampaignType;
import com.day.cq.commons.Filter;
import com.day.cq.commons.jcr.JcrUtil;
import com.day.cq.personalization.Location;
import com.day.cq.personalization.TargetedContentManager;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import com.day.cq.wcm.webservicesupport.Configuration;
import com.google.common.collect.ImmutableList;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.AbstractResourceVisitor;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Service
@Component
public class TargetHelperServiceImpl
implements TargetHelperService {
    private static final Logger LOG = LoggerFactory.getLogger(TargetHelperServiceImpl.class);
    private static final String RT_OFFER = "cq/personalization/components/teaserpage";
    private static final String RT_EXPERIENCE = "cq/personalization/components/experiencepage";
    private static final String RT_OFFER_PROXY = "cq/personalization/components/offerproxy";
    private static final String RT_TARGET_PARSYS = "cq/personalization/components/targetparsys";
    private static final String PN_LOCATION = "location";
    private static final String LOCATION_QUERY_TEMPLATE = "SELECT * FROM [nt:unstructured] as N WHERE ISDESCENDANTNODE(N,\"/content\") AND N.[sling:resourceType]=\"cq/personalization/components/target\" AND N.location= \"{0}\"";
    private static final String PN_TARGET_ENGINE = "cq:targetEngine";
    private static final String ENGINE_TNT = "tnt";
    @Reference
    private CryptoSupport cryptoSupport;
    @Reference
    private TargetedContentManager targetedContentManager;

    @Override
    public List<String> pushDefaultWinningExperience(Resource campaign) throws TestandtargetException {
        LOG.debug("Pushing default experience as a winner for campaign {}", (Object)campaign.getName());
        List locations = this.targetedContentManager.getCampaignLocations(campaign.getResourceResolver(), campaign.getPath());
        ArrayList<String> replacedLocations = new ArrayList<String>();
        try {
            for (Location loc : locations) {
                this.replaceWithDefaultContent(campaign.getResourceResolver(), loc.getPath());
                replacedLocations.add(loc.getPath());
            }
        }
        catch (RepositoryException e) {
            LOG.error(e.getMessage(), (Throwable)e);
            throw new TestandtargetException((Throwable)e);
        }
        catch (PersistenceException e) {
            LOG.error(e.getMessage(), (Throwable)e);
            throw new TestandtargetException((Throwable)e);
        }
        return replacedLocations == null ? new ArrayList<String>() : replacedLocations;
    }

    @Override
    public List<String> pushWinningExperience(Resource campaign, String experienceName) throws TestandtargetException {
        LOG.debug("Pushing experience {} as the winner for campaign at {}", (Object)experienceName, (Object)campaign.getPath());
        String experiencePath = this.findExperienceByName(campaign, experienceName);
        ResourceResolver resolver = campaign.getResourceResolver();
        Page experiencePage = ((PageManager)resolver.adaptTo(PageManager.class)).getPage(experiencePath);
        Iterator offers = experiencePage.listChildren((Filter)new Filter<Page>(){

            public boolean includes(Page page) {
                return page.getContentResource().isResourceType("cq/personalization/components/teaserpage") || page.getContentResource().isResourceType("cq/personalization/components/offerproxy");
            }
        });
        ArrayList<String> replacedPaths = new ArrayList<String>();
        try {
            while (offers.hasNext()) {
                Page offer = (Page)offers.next();
                LOG.debug("Processing offer at {}", (Object)offer.getPath());
                ValueMap properties = offer.getProperties();
                String location = (String)properties.get("location", (Object)"");
                if (StringUtils.isEmpty((String)location)) continue;
                List<String> locationPaths = this.searchPathsForLocation(resolver, location);
                LOG.warn("{} paths found for location {}", (Object)locationPaths.size(), (Object)location);
                Resource offerContent = this.getOfferContent(offer);
                if (offerContent == null) {
                    LOG.warn("Offer at path {} has no content (i.e. no 'par' node)", (Object)offer.getPath());
                    continue;
                }
                for (String locationPath : locationPaths) {
                    LOG.debug("Replacing content at path {}", (Object)locationPath);
                    this.copyOfferContent(resolver, offerContent, locationPath);
                    replacedPaths.add(locationPath);
                }
            }
            return replacedPaths == null ? new ArrayList<String>() : replacedPaths;
        }
        catch (RepositoryException e) {
            throw new TestandtargetException((Throwable)e);
        }
        catch (PersistenceException e) {
            throw new TestandtargetException((Throwable)e);
        }
    }

    @Override
    public Set<Resource> getTargetCampaigns(Resource parent) {
        TargetActivityProvider activityProvider = new TargetActivityProvider();
        activityProvider.accept(parent);
        return activityProvider.getResources();
    }

    @Override
    public List<CampaignType> getAvailableCampaignTypes(Configuration configuration) {
        LOG.debug("Retrieving available campaign types for configuration at {}", (Object)configuration.getPath());
        String accountOptions = (String)configuration.get("accountOptions", (Object)"");
        if (StringUtils.isEmpty((String)accountOptions)) {
            LOG.debug("accountOptions property not found or empty, returning default list");
            return Arrays.asList(CampaignType.values());
        }
        ArrayList<CampaignType> availableTypes = new ArrayList<CampaignType>();
        try {
            String actualOptions = this.cryptoSupport.unprotect(accountOptions);
            availableTypes.add(CampaignType.LANDING_PAGE);
            String[] opts = actualOptions.split(",");
            if (!Arrays.asList(opts).contains("xt_only")) {
                availableTypes.add(CampaignType.AB);
            }
            return availableTypes;
        }
        catch (CryptoException e) {
            LOG.error(e.getMessage(), (Throwable)e);
            return ImmutableList.of((Object)((Object)CampaignType.LANDING_PAGE));
        }
    }

    private Resource getOfferContent(Page offer) {
        LOG.debug("Retrieving content for offer at {}", (Object)offer.getPath());
        Resource contentResource = offer.getContentResource();
        if (contentResource.isResourceType("cq/personalization/components/teaserpage")) {
            return contentResource.getChild("par");
        }
        if (contentResource.isResourceType("cq/personalization/components/offerproxy")) {
            ValueMap properties = offer.getProperties();
            String offerPath = (String)properties.get("offerPath", (Object)"");
            if (StringUtils.isEmpty((String)offerPath)) {
                LOG.warn("The offer at {} is from the offer library, but the offerPath property is empty", (Object)offer.getPath());
                return null;
            }
            LOG.debug("The offer at {} is from the offer library. Retrieving content from {}", (Object)offer.getPath(), (Object)offerPath);
            Page libraryOfferPage = offer.getPageManager().getPage(offerPath);
            if (libraryOfferPage == null) {
                LOG.debug("Page at {} doesn't exist", (Object)offerPath);
                return null;
            }
            return libraryOfferPage.getContentResource().getChild("par");
        }
        return null;
    }

    private List<String> searchPathsForLocation(ResourceResolver resolver, String locationName) {
        LOG.debug("Searching for path of the location {}", (Object)locationName);
        locationName = StringUtils.replace((String)locationName, (String)"--author", (String)"");
        String queryString = MessageFormat.format("SELECT * FROM [nt:unstructured] as N WHERE ISDESCENDANTNODE(N,\"/content\") AND N.[sling:resourceType]=\"cq/personalization/components/target\" AND N.location= \"{0}\"", locationName);
        LOG.debug("Executing query {}", (Object)queryString);
        LOG.debug("Retrieving results...");
        ArrayList<String> paths = new ArrayList<String>();
        Iterator resIterator = resolver.findResources(queryString, "JCR-SQL2");
        while (resIterator.hasNext()) {
            paths.add(((Resource)resIterator.next()).getPath());
        }
        return paths;
    }

    private void replaceWithDefaultContent(ResourceResolver resolver, String path) throws RepositoryException, PersistenceException {
        LOG.debug("Replacing default content at path {}", (Object)path);
        Resource target = resolver.getResource(path);
        Resource parent = target.getParent();
        Resource def = target.getChild("default");
        Node newNode = JcrUtil.copy((Node)((Node)def.adaptTo(Node.class)), (Node)((Node)parent.adaptTo(Node.class)), (String)def.getName(), (boolean)true);
        LOG.debug("Created new node at {}", (Object)newNode.getPath());
        resolver.delete(target);
        resolver.commit();
        LOG.debug("Renaming new node to {}", (Object)path);
        Session s = (Session)resolver.adaptTo(Session.class);
        s.move(newNode.getPath(), path);
        s.save();
        LOG.debug("Session saved");
    }

    private String findExperienceByName(Resource campaign, String experienceName) {
        Page campaignPage = (Page)campaign.adaptTo(Page.class);
        Iterator experiences = campaignPage.listChildren((Filter)new Filter<Page>(){

            public boolean includes(Page element) {
                return element.getContentResource().isResourceType("cq/personalization/components/experiencepage");
            }
        });
        while (experiences.hasNext()) {
            Page experience = (Page)experiences.next();
            if (!experienceName.equals(experience.getTitle())) continue;
            return experience.getPath();
        }
        return null;
    }

    private void copyOfferContent(ResourceResolver resolver, Resource offerContent, String locationPath) throws PersistenceException, RepositoryException {
        Resource locationResource = offerContent.getResourceResolver().getResource(locationPath);
        if (locationResource == null) {
            throw new RuntimeException("Resource not found at path " + locationPath);
        }
        Resource destination = locationResource.getParent();
        LOG.debug("Destination is {}", (Object)destination);
        String nextSiblingName = this.detectNextSiblingName(locationResource);
        boolean originalComponentGone = false;
        Iterator children = offerContent.listChildren();
        while (children.hasNext()) {
            Resource kid = (Resource)children.next();
            if (!kid.isResourceType("cq/personalization/components/targetparsys")) {
                if (!originalComponentGone) {
                    LOG.debug("Deleting resource at {}", (Object)locationPath);
                    resolver.delete(locationResource);
                    resolver.commit();
                }
                LOG.debug("Copying resource at {} to {}", (Object)kid.getPath(), (Object)destination.getPath());
                Node newNode = JcrUtil.copy((Node)((Node)kid.adaptTo(Node.class)), (Node)((Node)destination.adaptTo(Node.class)), (String)null);
                if (StringUtils.isNotEmpty((String)nextSiblingName)) {
                    LOG.debug("Moving new node {} before {}", (Object)newNode.getName(), (Object)nextSiblingName);
                    newNode.getParent().orderBefore(newNode.getName(), nextSiblingName);
                }
                resolver.commit();
                LOG.debug("Resource copied successfully");
                originalComponentGone = true;
                continue;
            }
            LOG.warn("Won't copy node at {} because it's a target parsys", (Object)kid.getPath());
        }
    }

    protected String detectNextSiblingName(Resource resource) {
        Resource parent = resource.getParent();
        if (parent == null) {
            return "";
        }
        Iterator it = parent.listChildren();
        while (it.hasNext()) {
            Resource r = (Resource)it.next();
            if (!r.getName().equals(resource.getName())) continue;
            return it.hasNext() ? ((Resource)it.next()).getName() : "";
        }
        return "";
    }

    protected void bindCryptoSupport(CryptoSupport cryptoSupport) {
        this.cryptoSupport = cryptoSupport;
    }

    protected void unbindCryptoSupport(CryptoSupport cryptoSupport) {
        if (this.cryptoSupport == cryptoSupport) {
            this.cryptoSupport = null;
        }
    }

    protected void bindTargetedContentManager(TargetedContentManager targetedContentManager) {
        this.targetedContentManager = targetedContentManager;
    }

    protected void unbindTargetedContentManager(TargetedContentManager targetedContentManager) {
        if (this.targetedContentManager == targetedContentManager) {
            this.targetedContentManager = null;
        }
    }

    private class TargetActivityProvider
    extends AbstractResourceVisitor {
        Set<Resource> resources;

        private TargetActivityProvider() {
            this.resources = new HashSet<Resource>();
        }

        protected void visit(Resource resource) {
            if (this.includeResource(resource)) {
                this.resources.add(resource.getParent());
            }
        }

        public Set<Resource> getResources() {
            return this.resources;
        }

        private boolean includeResource(Resource resource) {
            boolean isCampaign = "cq/personalization/components/campaignpage".equals(resource.getResourceType());
            boolean isTargetEngine = "tnt".equals(resource.getValueMap().get((Object)"cq:targetEngine"));
            boolean isSynced = resource.getValueMap().get((Object)"cq:authorExternalId") != null;
            return isCampaign && isTargetEngine && isSynced;
        }
    }

}