All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.cq.msm.ui.models.alllivecopies.LiveCopiesTable Maven / Gradle / Ivy

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2016 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/


package com.adobe.cq.msm.ui.models.alllivecopies;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;

import javax.annotation.PostConstruct;
import javax.jcr.RangeIterator;

import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.util.Text;
import org.apache.sling.api.SlingHttpServletRequest;
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.commons.json.JSONArray;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.io.JSONStringer;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.Self;

import com.adobe.cq.wcm.launches.utils.LaunchUtils;
import com.adobe.granite.ui.components.Config;
import com.adobe.granite.ui.components.ExpressionHelper;
import com.adobe.granite.ui.components.ExpressionResolver;
import com.adobe.granite.xss.XSSAPI;
import com.day.cq.commons.jcr.JcrConstants;
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.BlueprintManager;
import com.day.cq.wcm.msm.api.LiveRelationship;
import com.day.cq.wcm.msm.api.LiveRelationshipManager;
import com.day.cq.wcm.msm.api.LiveStatus;
import com.day.cq.wcm.msm.api.MSMNameConstants;

@Model(adaptables = SlingHttpServletRequest.class)
public class LiveCopiesTable {

    @Self
    private SlingHttpServletRequest request;

    private Resource resource;
    private Resource rootResource;
    private ResourceResolver resourceResolver;
    private List rows;


    private PageManager pageManager;

    private LiveRelationshipManager relationMgr;

    private BlueprintManager bpm;


    private Map tableAttrbsMap;

    private boolean tableExists;


    private Map liveCopiesMap;

    private Pattern excludePattern;

    @ScriptVariable
    private XSSAPI xssAPI;

    @OSGiService
    private ExpressionResolver expressionResolver;

    private static HashMap statusDetailsMap = new HashMap();

    private static final String MSM_STATUS_STNCHRONIZED= "msm-status-synchronized";

    private static final String MSM_STATUS_SOURCE_MODIFIED = "msm-status-src-modified";

    private static final String MSM_STATUS_TARGET_MODIFIED = "msm-status-target-modified";

    private static final String MSM_STATUS_NEW = "msm-status-new";

    private static final String MSM_STATUS_TARGET_DOES_NOT_EXIST = "msm-status-target-does-not-exist";

    private static final String MSM_STATUS_CANCELLED = "msm-status-cancelled";

    private static final String MSM_BLUEPRINT_EDIT_PAGE_ACTION = "cq-wcm-msm-all-live-copies-openpage-activator";

    private static final String MSM_BLUEPRINT_ROLLOUT_ACTION = "cq-wcm-msm-all-live-copies-rollout-activator";

    private static final String MSM_LIVECOPY_EDIT_PAGE_ACTION = "cq-wcm-msm-all-live-copies-openlivecopy-activator";

    private static final String MSM_LIVECOPY_RELATIONSHIP_ACTION = "cq-wcm-msm-all-live-copies-relationship-activator";

    private static final String MSM_LIVECOPY_SYNC_ACTION = "cq-wcm-msm-all-live-copies-sync-activator";

    private static final String MSM_LIVECOPY_RESET_ACTION = "cq-wcm-msm-all-live-copies-reset-activator";

    private static final String MSM_LIVECOPY_SUSPEND_ACTION = "cq-wcm-msm-all-live-copies-suspend-activator";

    private static final String MSM_LIVECOPY_RESUME_ACTION = "cq-wcm-msm-all-live-copies-resume-activator";

    private static final String MSM_LIVECOPY_DETACH_ACTION = "cq-wcm-msm-all-live-copies-detach-activator";

    static {
        // for localization
        // i18n.get("LIVE COPY UP TO DATE");
        // i18n.get("BLUEPRINT MODIFIED");
        // i18n.get("LIVE COPY MODIFIED")
        // i18n.get("LIVE COPY LOCALLY CREATED");
        // i18n.get("LIVE COPY DOES NOT EXIST");
        // i18n.get("INHERITANCE CANCELLED");
        statusDetailsMap.put(MSM_STATUS_STNCHRONIZED, new StatusDetails("checkCircle", "cq-icon-green", "LIVE COPY UP TO DATE"));
        statusDetailsMap.put(MSM_STATUS_SOURCE_MODIFIED, new StatusDetails("clock", "cq-icon-black", "BLUEPRINT MODIFIED"));
        statusDetailsMap.put(MSM_STATUS_TARGET_MODIFIED, new StatusDetails("clock", "cq-icon-black", "LIVE COPY MODIFIED"));
        statusDetailsMap.put(MSM_STATUS_NEW, new StatusDetails("addCircle", "cq-icon-black", "LIVE COPY LOCALLY CREATED"));
        statusDetailsMap.put(MSM_STATUS_TARGET_DOES_NOT_EXIST, new StatusDetails("exclude", "cq-icon-grey", "LIVE COPY DOES NOT EXIST"));
        statusDetailsMap.put(MSM_STATUS_CANCELLED, new StatusDetails("closeCircle", "cq-icon-red", "INHERITANCE CANCELLED"));

    }


    @PostConstruct
    public void postConstruct() throws Exception {

        this.resource = request.getResource();
        this.resourceResolver = request.getResourceResolver();
        relationMgr = resourceResolver.adaptTo(LiveRelationshipManager.class);
        bpm = resourceResolver.adaptTo(BlueprintManager.class);
        pageManager = resourceResolver.adaptTo(PageManager.class);
        ExpressionHelper exp = new ExpressionHelper(expressionResolver, request);
        Config cfg = new Config(resource);
        String rootPath = StringUtils.trimToNull(request.getRequestPathInfo().getSuffix());
        this.rootResource = resourceResolver.getResource(rootPath);

        if (rootResource == null) {
            tableExists = false;
            return;
        }

        String filter = cfg.get("exclude", "");
        excludePattern = Pattern.compile(filter);

        tableExists = true;
        String src = StringUtils.trimToNull(exp.getString(cfg.get("src", String.class)));
        String layoutName = "foundation-layout-table";
        String selectionCount = cfg.get("selectionCount", "multiple");
        tableAttrbsMap = new HashMap();
        tableAttrbsMap.put("data-foundation-collection-id", rootPath);
        tableAttrbsMap.put("data-foundation-collection-src", src);
        tableAttrbsMap.put("data-foundation-selections-mode", selectionCount);
        tableAttrbsMap.put("data-foundation-mode-group", cfg.get("modeGroup", String.class));
        tableAttrbsMap.put("data-foundation-layout-table-hasmore", "false");

        String layoutJson = new JSONStringer()
                .object()
                .key("name").value(layoutName)
                .key("sortMode").value(cfg.get("sortMode", String.class))
                .key("layoutId").value(resource.getName()) // This is used as an id to identify the layout when there are multiple layouts to represent the same collection.
                .endObject()
                .toString();

        tableAttrbsMap.put("class", cfg.get("granite:rel", "cq-wcm-msm-all-live-copies") + " foundation-layout-util-maximized-alt foundation-collection foundation-layout-table coral-Table-wrapper coral-Table-wrapper--sticky " + layoutName);

        tableAttrbsMap.put("data-foundation-layout", layoutJson);
        if ("multiple".equals(selectionCount)) {
            tableAttrbsMap.put("multiple", selectionCount);
            tableAttrbsMap.put("selectionMode", cfg.get("selectionMode", "none"));
        }
        tableAttrbsMap.put("selectable", "selectable");

        RangeIterator rootRelations = relationMgr.getLiveRelationships(rootResource, null, null);

        liveCopiesMap = getFilteredNames(rootRelations);

        rows = new ArrayList();
        Page requestedPage = pageManager.getPage(rootResource.getPath());

        if (requestedPage == null) {
            //resource is folder list only folder contains

            Iterator resourceIterator = rootResource.getChildren().iterator();

            while (resourceIterator.hasNext()) {

                Resource currResource = resourceIterator.next();
                String relSourcePath = getRelativeSourcePath(rootResource, currResource);
                if ( !isResourceAcceptable(currResource)) {
                    continue;
                }


                Page currPage = pageManager.getPage(currResource.getPath());
                if (currPage == null) {
                    //row represents a folder;
                    rows.add(new LiveCopiesRow(currResource.getPath(), currResource.getName(), relSourcePath));
                } else {

                    String rowActionRels = getBlueprintActionRels(currResource, isResourceBlueprint(currResource));
                    String thumbnailPath = xssAPI.getValidHref(request.getContextPath() + getThumbnailUrl(currPage, 48, 48));
                    rows.add(new LiveCopiesRow(true, currResource.getPath(), currPage.getTitle(), Collections.emptyList(), thumbnailPath, rowActionRels, relSourcePath));
                }

            }

        } else {
            Iterator pageIter = requestedPage.listChildren();

            while (pageIter.hasNext()) {
                Page currPage = pageIter.next();
                Resource currPageResource = resourceResolver.getResource(currPage.getPath());
                String relSourcePath = getRelativeSourcePath(rootResource, currPageResource);
                if ( !isResourceAcceptable(currPageResource)) {
                    continue;
                }

                String relPath = "";
                if (currPage.getPath().startsWith(rootPath + "/")) {
                    relPath = currPage.getPath().substring(rootPath.length()+1);
                }

                String rowTitle = currPage.getTitle();
                String rowPath = currPage.getPath();
                String thumbnailPath = xssAPI.getValidHref(request.getContextPath() + getThumbnailUrl(currPage, 48, 48));
                List cellInfo = new ArrayList();
                Map cellAttrbs;
                boolean isBlueprint = false;

                for(String liveCopyPath : liveCopiesMap.keySet()) {

                    if ( !StringUtils.isEmpty(liveCopyPath)) {
                        cellAttrbs = new HashMap();
                        cellAttrbs.put("is", "coral-table-cell");
                        cellAttrbs.put("is", "coral-table-cell");
                        String currliveCopyPath = liveCopyPath + "/" + relPath;
                        String statusMesg = getStatus(relationMgr, currPageResource, currliveCopyPath);

                        if (!isBlueprint && bpm.getContainingBlueprint(currPageResource.getPath()) != null &&
                                 statusMesg != MSM_STATUS_TARGET_DOES_NOT_EXIST) {

                                isBlueprint = true;
                        }

                        StatusDetails statusDetails = statusDetailsMap.get(statusMesg);
                        String cellActionRels = getLivecopyActionRels(statusMesg);
                        String relationshipDetailJson = new JSONStringer()
                                .object()
                                .key("source").value(currPage.getPath())
                                .key("target").value(currliveCopyPath)
                                .endObject()
                                .toString();
                        cellAttrbs.put("data-relationship", relationshipDetailJson);
                        cellAttrbs.put("data-actionrels", cellActionRels);
                        LiveCopiesCell allLiveCopiesCell = new LiveCopiesCell(statusDetails.getIconName(), statusDetails.getIconClass(), statusDetails.getStatusMessage(), cellAttrbs);
                        cellInfo.add(allLiveCopiesCell);
                    }

                }

                // root resource does not have live copies but current row resource might have
                if (!isBlueprint && liveCopiesMap.isEmpty()) {
                    isBlueprint = isResourceBlueprint(currPageResource);
                }
                String rowActionRels = getBlueprintActionRels(currPageResource, isBlueprint);
                rows.add(new LiveCopiesRow(true, rowPath, rowTitle, cellInfo, thumbnailPath, rowActionRels, relSourcePath));
            }

        }
    }

    private boolean isResourceAcceptable(Resource currResource) {

        if (excludePattern.matcher(currResource.getPath()).matches()) {
            return false;
        } else if (pageManager.getPage(currResource.getPath()) != null) {
            return true;
        } else {
            boolean isFolder = currResource.isResourceType("sling:Folder") ||
                    currResource.isResourceType("sling:OrderedFolder") ||
                    currResource.isResourceType(JcrConstants.NT_FOLDER);

            return isFolder;
        }

    }

    private boolean isResourceBlueprint(Resource currResource) throws WCMException {

        if (bpm.getContainingBlueprint(currResource.getPath()) == null ||
                !relationMgr.isSource(currResource)) {
            return false;
        }

        RangeIterator relationships = relationMgr.getLiveRelationships(currResource, null, null);
        while(relationships.hasNext()) {
            LiveRelationship currRelationship = (LiveRelationship) relationships.next();
            String lcPath = currRelationship.getLiveCopy().getPath();
            if (!LaunchUtils.isLaunchResourcePath(lcPath)) {
                // at least one is not a launch > condition true
                return true;
            }
        }

        return false;
    }

    private String getBlueprintActionRels(Resource currResource, boolean isBlueprint) {
        String rowActionRels = MSM_BLUEPRINT_EDIT_PAGE_ACTION;
        if (isBlueprint) {
            rowActionRels = rowActionRels + " " + MSM_BLUEPRINT_ROLLOUT_ACTION;
        }

        return  rowActionRels;
    }

    private String getLivecopyActionRels(String statusMesg) throws JSONException {

        JSONArray actionRels = new JSONArray();
        actionRels.put(MSM_LIVECOPY_EDIT_PAGE_ACTION);
        actionRels.put(MSM_LIVECOPY_RELATIONSHIP_ACTION);
        if (statusMesg.equals(MSM_STATUS_TARGET_DOES_NOT_EXIST)) {

        } else if (statusMesg.equals(MSM_STATUS_CANCELLED)) {
            actionRels.put(MSM_LIVECOPY_RESUME_ACTION)
                      .put(MSM_LIVECOPY_DETACH_ACTION);
        } else {
            actionRels.put(MSM_LIVECOPY_SYNC_ACTION)
                      .put(MSM_LIVECOPY_RESET_ACTION)
                      .put(MSM_LIVECOPY_SUSPEND_ACTION)
                      .put(MSM_LIVECOPY_DETACH_ACTION);
        }
        return actionRels.toString();
    }





    private String getThumbnailUrl(Page page, int width, int height) {
        String ck = "";

        ValueMap metadata = page.getProperties("image/file/jcr:content");
        if (metadata != null) {
            Calendar cal = metadata.get(JcrConstants.JCR_LASTMODIFIED, Calendar.class);
            if (cal != null) {
                ck = "" + (cal.getTimeInMillis() / 1000);
            }
        }

        return Text.escapePath(page.getPath()) + ".thumb." + width + "." + height + ".png?ck=" + ck;
    }

    public String getPath() {
        return this.resource.getPath();
    }

    public Map getColumns() {
        return liveCopiesMap;
    }

    public List getRows() {
        return rows;
    }

    public Map getTableAttrbs() {
        return tableAttrbsMap;
    }

    public boolean tableExist() {
        return tableExists;
    }

    public boolean hasChildren() {
        if (rows.isEmpty()) {
            return false;
        }
        return true;
    }

    public int columnsSize() {
        return liveCopiesMap.size() + 2 ;
    }


    private String getRelativeSourcePath(Resource rootResource, Resource currResource) {
        String relSourcePath = currResource.getPath().replaceFirst(rootResource.getPath(), "");
        if (relSourcePath.startsWith("/")) {
            relSourcePath = relSourcePath.replaceFirst("/", "");
        }
        return relSourcePath;
    }
    private Map getFilteredNames(RangeIterator rootRelations) {
        Map names = new TreeMap();
        String previousPath = null;
        String[] prefix = null;
        String prefixStr = "";
        while (rootRelations.hasNext()) {
            LiveRelationship relation = (LiveRelationship) rootRelations.next();
            String lcPath = relation.getLiveCopy().getPath();
            if(prefix == null) {
                //first time: initialize prefix
                prefix = Text.explode(lcPath, '/');
            } else {
                //prefixStr will always contain the last computed prefix
                prefixStr = "";
                String[] pathExploded = Text.explode(lcPath, '/');
                //find common root with prefix
                int i = 0;
                for(; i< prefix.length && i < pathExploded.length; i++) {
                    if(prefix[i] == null || !pathExploded[i].equals(prefix[i])) {
                        break;
                    }
                    prefixStr += "/" + prefix[i];
                }
                //previous loop must find first exclusion index; remove from suffix everything after i
                for(; i < prefix.length; i++) {
                    prefix[i] = null;
                }
            }

            names.put(relation.getTargetPath(),lcPath);
        }

        //names should contain non filter pathes --> remove prefix from pathes
        for (String key : names.keySet()) {
            String path = names.get(key);
            path = path.substring(prefixStr.length() + 1);
            if(path.startsWith("content/")) {
                path = path.substring(8);
            }
            names.put(key, path);
        }

        return names;
    }

    private String getStatus(LiveRelationshipManager relationMgr, Resource source, String targetFilter) throws Exception {

        RangeIterator pageRelations = relationMgr.getLiveRelationships(source, targetFilter, null);
        if (pageRelations == null) {
            return MSM_STATUS_TARGET_DOES_NOT_EXIST;
        }

        while (pageRelations.hasNext()) {
            LiveRelationship currRelationship = (LiveRelationship) pageRelations.next();
            if (currRelationship.getTargetPath().equals(targetFilter)) {
                LiveStatus status = currRelationship.getStatus();
                Map advanceStatus = status.getAdvancedStatus();
                if (!status.isCancelled()) {
                    if (status.isTargetExisting() && status.isSourceExisting() && !getAdvanceStatusValue(advanceStatus, MSMNameConstants.PARAM_IS_TARGET_MANUALLY_CREATED)) {

                        if (getAdvanceStatusValue(advanceStatus, MSMNameConstants.PARAM_IS_SOURCE_MODIFIED)) {
                            return MSM_STATUS_SOURCE_MODIFIED;
                        } else {

                            if ( getAdvanceStatusValue(advanceStatus, MSMNameConstants.PARAM_IS_TARGET_MODIFIED)) {
                                return MSM_STATUS_TARGET_MODIFIED;

                            } else {
                                return MSM_STATUS_STNCHRONIZED;
                            }

                        }

                    } else {

                        if (status.isSourceExisting()) {

                            if (getAdvanceStatusValue(advanceStatus, MSMNameConstants.PARAM_IS_TARGET_MANUALLY_CREATED)) {
                                return MSM_STATUS_NEW;
                            } else {
                                return MSM_STATUS_TARGET_DOES_NOT_EXIST;
                            }

                        } else if (getAdvanceStatusValue(advanceStatus, MSMNameConstants.PARAM_IS_SOURCE_DELETED)) {
                            return MSM_STATUS_CANCELLED;
                        }
                    }

                } else {
                    if ( getAdvanceStatusValue(advanceStatus, MSMNameConstants.PARAM_IS_SOURCE_EXISTING) || !getAdvanceStatusValue(advanceStatus, MSMNameConstants.PARAM_IS_SOURCE_DELETED)) {
                        return MSM_STATUS_CANCELLED;
                    }

                }
            }

        }

        return MSM_STATUS_TARGET_DOES_NOT_EXIST;
    }



    private boolean getAdvanceStatusValue(Map advanceStatus, String param) {
        if (advanceStatus.containsKey(param) && advanceStatus.get(param)) {
            return true;
        }
        return false;
    }
    
    
    private static class StatusDetails {
        
        private String iconName;
        private String iconClass;
        private String statusMessage;
        
        private StatusDetails (String iconName, String iconClass, String statusMessage) {
            this.iconClass = iconClass;
            this.iconName = iconName;
            this.statusMessage = statusMessage;
            
        }
        
        public String getIconName() {
            return iconName;
        }
        
        public String getIconClass() {
            return iconClass;
        }
        
        public String getStatusMessage() {
            return statusMessage;
        }
     }



}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy