Package org.jasig.portal.url

Source Code of org.jasig.portal.url.SingleTabUrlNodeSyntaxHelper

/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you 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 org.jasig.portal.url;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;
import javax.xml.xpath.XPathExpression;

import org.apache.commons.lang.StringUtils;
import org.jasig.portal.IUserPreferencesManager;
import org.jasig.portal.PortalException;
import org.jasig.portal.concurrency.caching.RequestCache;
import org.jasig.portal.dao.usertype.FunctionalNameType;
import org.jasig.portal.layout.IStylesheetUserPreferencesService;
import org.jasig.portal.layout.IStylesheetUserPreferencesService.PreferencesScope;
import org.jasig.portal.layout.IUserLayout;
import org.jasig.portal.layout.IUserLayoutManager;
import org.jasig.portal.layout.PortletTabIdResolver;
import org.jasig.portal.layout.node.IUserLayoutNodeDescription;
import org.jasig.portal.layout.om.IStylesheetDescriptor;
import org.jasig.portal.layout.om.IStylesheetParameterDescriptor;
import org.jasig.portal.portlet.om.IPortletDefinition;
import org.jasig.portal.portlet.om.IPortletEntity;
import org.jasig.portal.portlet.om.IPortletEntityId;
import org.jasig.portal.portlet.om.IPortletWindow;
import org.jasig.portal.portlet.om.IPortletWindowId;
import org.jasig.portal.portlet.registry.IPortletEntityRegistry;
import org.jasig.portal.portlet.registry.IPortletWindowRegistry;
import org.jasig.portal.user.IUserInstance;
import org.jasig.portal.user.IUserInstanceManager;
import org.jasig.portal.xml.xpath.XPathOperations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.common.base.Function;

/**
* Maps tabs and portlets to folder names and back. Handles a single set of tabs and uses tab IDs for folder names.
*
* @author Eric Dalquist
* @version $Revision$
*/
@Service
public class SingleTabUrlNodeSyntaxHelper implements IUrlNodeSyntaxHelper {
    public static final String EXTERNAL_ID_ATTR = "externalId";

    private static final char PORTLET_PATH_ELEMENT_SEPERATOR = '.';
   
    protected final Logger logger = LoggerFactory.getLogger(getClass());
   
    private String defaultLayoutNodeIdExpression = "/layout/folder/folder[@type='regular' and @hidden!='true'][$defaultTab]/@ID";
//    private String tabIdExpression = "/layout/folder/folder[@ID=$nodeId or descendant::node()[@ID=$nodeId]]/@ID";
    private String defaultTabParameter = "defaultTab";
   
    private IUserInstanceManager userInstanceManager;
    private XPathOperations xpathOperations;
    private IStylesheetUserPreferencesService stylesheetUserPreferencesService;
    private IPortletWindowRegistry portletWindowRegistry;
    private IPortletEntityRegistry portletEntityRegistry;
   
    @Autowired
    public void setPortletWindowRegistry(IPortletWindowRegistry portletWindowRegistry) {
        this.portletWindowRegistry = portletWindowRegistry;
    }

    @Autowired
    public void setPortletEntityRegistry(IPortletEntityRegistry portletEntityRegistry) {
        this.portletEntityRegistry = portletEntityRegistry;
    }

    @Autowired
    public void setUserInstanceManager(IUserInstanceManager userInstanceManager) {
        this.userInstanceManager = userInstanceManager;
    }

    @Autowired
    public void setXpathOperations(XPathOperations xpathOperations) {
        this.xpathOperations = xpathOperations;
    }
   
    @Autowired
    public void setStylesheetUserPreferencesService(IStylesheetUserPreferencesService stylesheetUserPreferencesService) {
        this.stylesheetUserPreferencesService = stylesheetUserPreferencesService;
    }
   
    @Override
    public String getName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public String getDefaultLayoutNodeId(HttpServletRequest httpServletRequest) {
        final IUserInstance userInstance = this.userInstanceManager.getUserInstance(httpServletRequest);
        final IUserPreferencesManager preferencesManager = userInstance.getPreferencesManager();
        final IUserLayoutManager userLayoutManager = preferencesManager.getUserLayoutManager();

        final IUserLayout userLayout = userLayoutManager.getUserLayout();
       
        //This logic is specific to tab/column layouts
        final String defaultTabIndex = this.getDefaultTabIndex(httpServletRequest);
        if (defaultTabIndex != null) {
            final String defaultTabId = this.getTabId(userLayout, defaultTabIndex);
            if (StringUtils.isNotEmpty(defaultTabId)) {
                return defaultTabId;
            }
        }
   
        this.logger.warn("Failed to find default tab id for '" + userInstance.getPerson().getUserName() + "' with default tab index " + defaultTabIndex + ". Index 1 will be tried as a fall-back.");
       
        final String firstTabId = getTabId(userLayout, "1");
        if (StringUtils.isNotEmpty(firstTabId)) {
            return firstTabId;
        }
       
        this.logger.warn("Failed to find default tab id for '" + userInstance.getPerson().getUserName() + "' with default tab index 1. The user has no tabs.");
       
        return userLayout.getRootId();
    }

    protected String getTabId(final IUserLayout userLayout, final String tabIndex) {
        return this.xpathOperations.doWithExpression(
            defaultLayoutNodeIdExpression,
            Collections.singletonMap("defaultTab", tabIndex),
            new Function<XPathExpression, String>() {
                @Override
                public String apply(XPathExpression xPathExpression) {
                    return userLayout.findNodeId(xPathExpression);
                }
            });
    }

    /**
     * Get the index of the default tab for the user
     */
    protected String getDefaultTabIndex(HttpServletRequest httpServletRequest) {
        final String stylesheetParameter = this.stylesheetUserPreferencesService.getStylesheetParameter(httpServletRequest, PreferencesScope.STRUCTURE, this.defaultTabParameter);
        if (stylesheetParameter != null) {
            return stylesheetParameter;
        }
       
        final IStylesheetDescriptor stylesheetDescriptor = this.stylesheetUserPreferencesService.getStylesheetDescriptor(httpServletRequest, PreferencesScope.STRUCTURE);
        final IStylesheetParameterDescriptor stylesheetParameterDescriptor = stylesheetDescriptor.getStylesheetParameterDescriptor(this.defaultTabParameter);
        if (stylesheetParameterDescriptor != null) {
            return stylesheetParameterDescriptor.getDefaultValue();
        }
       
        return null;
    }

    @RequestCache(keyMask={false, true})
    @Override
    public List<String> getFolderNamesForLayoutNode(HttpServletRequest request, String layoutNodeId) {
        /*
         * Implementation note:
         * While the API allows one or more folder names, this implementation will only ever return
         * a List with zero or one element.  It's not entirely clear that a List with zero members
         * was allowed by the interface definition, but this implementation will return an empty
         * list if the layoutNodeId cannot be found in the layout.
         */


        final IUserInstance userInstance = this.userInstanceManager.getUserInstance(request);
        final IUserPreferencesManager preferencesManager = userInstance.getPreferencesManager();
        final IUserLayoutManager userLayoutManager = preferencesManager.getUserLayoutManager();
        final IUserLayout userLayout = userLayoutManager.getUserLayout();
       
        final String tabId = userLayout.findNodeId(new PortletTabIdResolver(layoutNodeId));
       
        if (StringUtils.isEmpty(tabId)) {
            return Collections.emptyList();
        }
       
        String externalId = stylesheetUserPreferencesService.getLayoutAttribute(request, PreferencesScope.STRUCTURE, tabId, EXTERNAL_ID_ATTR);
        if (externalId != null) {
            final Map<String, String> allNodesAndValuesForAttribute = stylesheetUserPreferencesService.getAllNodesAndValuesForAttribute(request, PreferencesScope.STRUCTURE, EXTERNAL_ID_ATTR);

            boolean appendNodeId = false;
            for (final Entry<String, String> nodeAttributeEntry : allNodesAndValuesForAttribute.entrySet()) {
                final String entryNodeId = nodeAttributeEntry.getKey();
                final String entryValue = nodeAttributeEntry.getValue();
                if (!tabId.equals(entryNodeId) && externalId.equals(entryValue)) {
                    appendNodeId = true;
                    break;
                }
            }
           
            if (!FunctionalNameType.isValid(externalId)) {
                logger.warn("ExternalId {} for tab {} is not a valid fname. It will be converted for use in the URL but this results in additional overhead", externalId, tabId);
                externalId = FunctionalNameType.makeValid(externalId);
            }
           
            if (appendNodeId) {
                externalId = externalId + PORTLET_PATH_ELEMENT_SEPERATOR + layoutNodeId;
            }

            logger.trace("Tab identified by {} resolved to externalId {} " +
                    "so returning that externalId as the sole folder name for node.", layoutNodeId, externalId);
           
            return Arrays.asList(externalId);

        } else {
            logger.trace("Tab identified by {} had no externalId " +
                    "so returning just {} as the sole folder name for node {}.", layoutNodeId, tabId, layoutNodeId);
            return Arrays.asList(tabId);
        }
    }

    @Override
    public String getLayoutNodeForFolderNames(HttpServletRequest request, List<String> folderNames) {
        /*
         * Implementation note: while the API specifies a List of folderNames, this implementation
         * only ever considers the first (presumably, only) value in the list.
         */

        if (folderNames == null || folderNames.isEmpty()) {
            logger.warn("Asked to get layout node for an empty or null folderNames ({}).", folderNames);
            return null;
        }

        //Check if the folder name is compound and parse it if it is
        String folderName = folderNames.get(0);
        String layoutNodeId = null;
        final int seperatorIndex = folderName.indexOf(PORTLET_PATH_ELEMENT_SEPERATOR);
        if (seperatorIndex > 0 && seperatorIndex < folderName.length() - 1) {
            layoutNodeId = folderName.substring(seperatorIndex + 1);
            folderName = folderName.substring(0, seperatorIndex);
        }

        if (folderNames.size() > 1) {
            logger.warn("Asked to consider multiple folder names {}, " +
                    "but ignoring all but the first which has been parsed as {}.", folderNames, folderName);
        }
       
        // Search the users layout attributes for a layout node with a matching externalId value
        String firstMatchingNodeId = null;
        final Map<String, String> allNodesAndValuesForAttribute = stylesheetUserPreferencesService.getAllNodesAndValuesForAttribute(request, PreferencesScope.STRUCTURE, EXTERNAL_ID_ATTR);
        for (final Entry<String, String> entry : allNodesAndValuesForAttribute.entrySet()) {
            final String value = entry.getValue();
            //Have to test against the fname safe version of the externalId since the folderName could have already been translated
            if (value.equals(folderName) || FunctionalNameType.makeValid(value).equals(folderName)) {
                final String nodeId = entry.getKey();
               
                if (nodeId.equals(layoutNodeId)) {
                    //ExternalId matched as well as the layoutNodeId, clear the firstMatchingNodeId since we found the nodeId here

                    logger.trace("Parsed folder names {} to nodeId {}.", folderNames, nodeId);

                    return nodeId;
                }
                else if (firstMatchingNodeId == null) {
                    firstMatchingNodeId = nodeId;
                }
            }
        }
       
        //If an explicit nodeId match isn't found but at least one matching externalId was found use that match
        if (firstMatchingNodeId != null) {
            layoutNodeId = firstMatchingNodeId;
        }
        //In this case the folderName must not have been an externalId, assume it is a layout node
        else if (layoutNodeId == null) {
            layoutNodeId = folderName;
        }
       
       
        //Verify the parsed layoutNodeId matches a node in the user's layout
        final IUserInstance userInstance = this.userInstanceManager.getUserInstance(request);
        final IUserPreferencesManager preferencesManager = userInstance.getPreferencesManager();
        final IUserLayoutManager userLayoutManager = preferencesManager.getUserLayoutManager();
       
        final IUserLayoutNodeDescription node;
        try {
            node = userLayoutManager.getNode(layoutNodeId);
        }
        catch (PortalException e) {

            logger.warn("Parsed requested folder names {} to layoutNodeId {} " +
                    "but did not match a node in the user layout.", folderNames, layoutNodeId, e);

            return null;
        }
       
        if (node == null) {

            logger.warn("Parsed requested folder names to layoutNodeId {} " +
                    "but did not match a node in the user layout.", folderNames, layoutNodeId);

            return null;
        }

        String nodeId = node.getId();

        logger.trace("Resolved node id {} for folder names {}.", nodeId, folderNames);
        return nodeId;
    }

    @RequestCache(keyMask={false, true})
    @Override
    public String getFolderNameForPortlet(HttpServletRequest request, IPortletWindowId portletWindowId) {
        final IPortletWindow portletWindow = this.portletWindowRegistry.getPortletWindow(request, portletWindowId);
        final IPortletEntity portletEntity = portletWindow.getPortletEntity();
        final IPortletDefinition portletDefinition = portletEntity.getPortletDefinition();
       
        final String fname = portletDefinition.getFName();
        final String layoutNodeId = portletEntity.getLayoutNodeId();
       
        //Build the targeted portlet string (fname + subscribeId)
        return fname + PORTLET_PATH_ELEMENT_SEPERATOR + layoutNodeId;
    }

   
  @Override
  public IPortletWindowId getPortletForFolderName(HttpServletRequest request, String targetedLayoutNodeId, String folderName) {
        //Basic parsing of the
        final String fname;
        String subscribeId = null;
        final int seperatorIndex = folderName.indexOf(PORTLET_PATH_ELEMENT_SEPERATOR);
        if (seperatorIndex <= 0 || seperatorIndex + 1 == folderName.length()) {
          fname = folderName;
        }
        else {
            fname = folderName.substring(0, seperatorIndex);
            subscribeId = folderName.substring(seperatorIndex + 1);
        }
       
        //If a subscribeId was provided validate that it matches up with the fname
        if (subscribeId != null) {
          final IUserInstance userInstance = this.userInstanceManager.getUserInstance(request);
        final IPortletEntity portletEntity = this.portletEntityRegistry.getOrCreatePortletEntity(request, userInstance, subscribeId);
        if (portletEntity == null || !fname.equals(portletEntity.getPortletDefinition().getFName())) {
          //If no entity found or the fname doesn't match ignore the provided subscribeId by setting it to null
          subscribeId = null;
        }
        else {
          //subscribeId matches fname, lookup the window for the entity and return the windowId
          final IPortletEntityId portletEntityId = portletEntity.getPortletEntityId();
          final IPortletWindow portletWindow = this.portletWindowRegistry.getOrCreateDefaultPortletWindow(request, portletEntityId);
          if (portletWindow == null) {
              return null;
          }

          return portletWindow.getPortletWindowId();
        }
        }
       
        //No valid subscribeId, find the best match based on the fname
       
        //If a layout node is targeted then look for a matching subscribeId under that targeted node
        if (targetedLayoutNodeId != null) {
          final IUserInstance userInstance = this.userInstanceManager.getUserInstance(request);
          final IUserPreferencesManager preferencesManager = userInstance.getPreferencesManager();
          final IUserLayoutManager userLayoutManager = preferencesManager.getUserLayoutManager();
         
          //First look for the layout node only under the specified folder
          subscribeId = userLayoutManager.getSubscribeId(targetedLayoutNodeId, fname);
        }

        //Find a subscribeId based on the fname
        final IPortletWindow portletWindow;
      if (subscribeId == null) {
        portletWindow = this.portletWindowRegistry.getOrCreateDefaultPortletWindowByFname(request, fname);
      }
      else {
        portletWindow = this.portletWindowRegistry.getOrCreateDefaultPortletWindowByLayoutNodeId(request, subscribeId);
      }
       
        if (portletWindow == null) {
          return null;
        }
       
        return portletWindow.getPortletWindowId();
    }

}
TOP

Related Classes of org.jasig.portal.url.SingleTabUrlNodeSyntaxHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.