/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.kernel.portlet;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.PortletConstants;
import com.liferay.portal.kernel.model.PortletInstance;
import com.liferay.portal.kernel.util.PortalUtil;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.Validator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.portlet.PortletMode;
import javax.portlet.WindowState;
/**
* The default friendly URL mapper to use with friendly URL routes.
*
* <p>
* In most cases, to add friendly URL mapping to a portlet, simply set this
* class as the friendly URL mapper in <code>liferay-portlet.xml</code>, and
* write a <code>friendly-url-routes.xml</code> file.
* </p>
*
* <p>
* If you do need to extend this class, the key methods to override are {@link
* #buildPath(LiferayPortletURL)} and {@link #populateParams(String, Map, Map)}.
* </p>
*
* @author Connor McKay
* @see Router
*/
public class DefaultFriendlyURLMapper extends BaseFriendlyURLMapper {
public DefaultFriendlyURLMapper() {
defaultIgnoredParameters = new LinkedHashSet<>();
defaultIgnoredParameters.add("p_p_id");
defaultReservedParameters = new LinkedHashMap<>();
defaultReservedParameters.put("p_p_lifecycle", "0");
defaultReservedParameters.put(
"p_p_state", WindowState.NORMAL.toString());
defaultReservedParameters.put("p_p_mode", PortletMode.VIEW.toString());
}
/**
* Adds a default ignored parameter.
*
* <p>
* A default ignored parameter will always be hidden in friendly URLs.
* </p>
*
* @param name the name of the parameter
*/
public void addDefaultIgnoredParameter(String name) {
defaultIgnoredParameters.add(name);
}
/**
* Adds a default reserved parameter.
*
* <p>
* A default reserved parameter will be hidden in friendly URLs when it is
* set to its default value.
* </p>
*
* @param name the name of the parameter
* @param value the default value of the parameter
*/
public void addDefaultReservedParameter(String name, String value) {
defaultReservedParameters.put(name, value);
}
@Override
public String buildPath(LiferayPortletURL liferayPortletURL) {
Map<String, String> routeParameters = new HashMap<>();
buildRouteParameters(liferayPortletURL, routeParameters);
String friendlyURLPath = router.parametersToUrl(routeParameters);
if (Validator.isNull(friendlyURLPath)) {
return null;
}
addParametersIncludedInPath(liferayPortletURL, routeParameters);
friendlyURLPath = StringPool.SLASH.concat(getMapping()).concat(
friendlyURLPath);
return friendlyURLPath;
}
/**
* Returns the default ignored parameters.
*
* @return the ignored parameter names
* @see #addDefaultIgnoredParameter(String)
*/
public Set<String> getDefaultIgnoredParameters() {
return defaultIgnoredParameters;
}
/**
* Returns the default reserved parameters.
*
* @return the default reserved parameter names and values
* @see #addDefaultReservedParameter(String, String)
*/
public Map<String, String> getDefaultReservedParameters() {
return defaultReservedParameters;
}
@Override
public void populateParams(
String friendlyURLPath, Map<String, String[]> parameterMap,
Map<String, Object> requestContext) {
friendlyURLPath = friendlyURLPath.substring(getMapping().length() + 1);
if (friendlyURLPath.endsWith(StringPool.SLASH)) {
friendlyURLPath = friendlyURLPath.substring(
0, friendlyURLPath.length() - 1);
}
Map<String, String> routeParameters = new HashMap<>();
if (!router.urlToParameters(friendlyURLPath, routeParameters)) {
if (_log.isWarnEnabled()) {
_log.warn(
"No route could be found to match URL " + friendlyURLPath);
}
return;
}
String namespace = null;
String portletInstanceKey = getPortletId(routeParameters);
if (Validator.isNotNull(portletInstanceKey)) {
namespace = PortalUtil.getPortletNamespace(portletInstanceKey);
addParameter(namespace, parameterMap, "p_p_id", portletInstanceKey);
}
else if (isAllPublicRenderParameters(routeParameters)) {
// Portlet namespace is not needed if all the parameters are public
// render parameters
addParameter(null, parameterMap, "p_p_id", getPortletId());
}
else {
return;
}
populateParams(parameterMap, namespace, routeParameters);
}
/**
* Adds the parameters included in the path to the portlet URL.
*
* <p>
* Portlet URLs track which parameters are included in the friendly URL
* path. This method hides all the default ignored parameters, the
* parameters included in the path by the router, and the reserved
* parameters set to their defaults.
* </p>
*
* @param liferayPortletURL the portlet URL to which to add the parameters
* included in the path
* @param routeParameters the parameter map populated by the router
* @see com.liferay.portlet.PortletURLImpl#addParameterIncludedInPath(
* String)
*/
protected void addParametersIncludedInPath(
LiferayPortletURL liferayPortletURL,
Map<String, String> routeParameters) {
// Hide default ignored parameters
for (String name : defaultIgnoredParameters) {
liferayPortletURL.addParameterIncludedInPath(name);
}
// Hide application parameters removed by the router
Map<String, String[]> portletURLParameters =
liferayPortletURL.getParameterMap();
for (String name : portletURLParameters.keySet()) {
if (!routeParameters.containsKey(name)) {
liferayPortletURL.addParameterIncludedInPath(name);
}
}
// Hide reserved parameters removed by the router or set to the defaults
liferayPortletURL.visitReservedParameters(
(key, value) -> {
if (!routeParameters.containsKey(key) ||
value.equals(defaultReservedParameters.get(key))) {
liferayPortletURL.addParameterIncludedInPath(key);
}
});
}
/**
* Builds the parameter map to be used by the router by copying parameters
* from the portlet URL.
*
* <p>
* This method also populates the special virtual parameters
* <code>p_p_id</code> and <code>instanceId</code> for instanceable
* portlets.
* </p>
*
* @param liferayPortletURL the portlet URL to copy parameters from
* @param routeParameters the parameter map to populate for use by the
* router
*/
protected void buildRouteParameters(
LiferayPortletURL liferayPortletURL,
Map<String, String> routeParameters) {
// Copy application parameters
Map<String, String[]> portletURLParameters =
liferayPortletURL.getParameterMap();
for (Map.Entry<String, String[]> entry :
portletURLParameters.entrySet()) {
String[] values = entry.getValue();
if (values.length > 0) {
routeParameters.put(entry.getKey(), values[0]);
}
}
// Populate virtual parameters for instanceable portlets
String portletInstanceKey = liferayPortletURL.getPortletId();
if (Validator.isNotNull(portletInstanceKey)) {
routeParameters.put("p_p_id", portletInstanceKey);
PortletInstance portletInstance =
PortletInstance.fromPortletInstanceKey(portletInstanceKey);
routeParameters.put(
"userIdAndInstanceId",
portletInstance.getUserIdAndInstanceId());
if (portletInstance.hasInstanceId()) {
routeParameters.put(
"instanceId", portletInstance.getInstanceId());
}
}
// Copy reserved parameters
liferayPortletURL.visitReservedParameters(routeParameters::put);
}
/**
* Returns the portlet ID, including the instance ID if applicable, from the
* parameter map.
*
* @param routeParameters the parameter map. For an instanceable
* portlet, this must contain either <code>p_p_id</code> or
* <code>instanceId</code>.
* @return the portlet ID, including the instance ID if applicable, or
* <code>null</code> if it cannot be determined
* @deprecated As of 7.0.0, replaced by {@link #getPortletInstanceKey(Map)}
*/
@Deprecated
protected String getPortletId(Map<String, String> routeParameters) {
return getPortletInstanceKey(routeParameters);
}
/**
* Returns the portlet instance key, including the instance ID if
* applicable, from the parameter map.
*
* @param routeParameters the parameter map. For an instanceable portlet,
* this must contain either <code>p_p_id</code> or
* <code>instanceId</code>.
* @return the portlet instance key, including the instance ID if
* applicable, or <code>null</code> if it cannot be determined
*/
protected String getPortletInstanceKey(
Map<String, String> routeParameters) {
String userIdAndInstanceId = routeParameters.remove(
"userIdAndInstanceId");
if (!isPortletInstanceable() && Validator.isNull(userIdAndInstanceId)) {
return getPortletId();
}
String portletInstanceKey = routeParameters.remove("p_p_id");
if (Validator.isNotNull(portletInstanceKey)) {
return portletInstanceKey;
}
if (Validator.isNotNull(userIdAndInstanceId)) {
PortletInstance portletInstance =
PortletInstance.fromPortletNameAndUserIdAndInstanceId(
getPortletId(), userIdAndInstanceId);
return portletInstance.getPortletInstanceKey();
}
String instanceId = routeParameters.remove("instanceId");
if (Validator.isNotNull(instanceId)) {
return PortletConstants.assemblePortletId(
getPortletId(), instanceId);
}
if (!isAllPublicRenderParameters(routeParameters)) {
_log.error(
"Either p_p_id or instanceId must be provided for an " +
"instanceable portlet");
}
return null;
}
/**
* Returns <code>true</code> if all the route parameters are public render
* parameters.
*
* @param routeParameters the parameter map
* @return <code>true</code> if all the route parameters are public render
* parameters; <code>false</code> otherwise
*/
protected boolean isAllPublicRenderParameters(
Map<String, String> routeParameters) {
Set<String> routeParameterKeys = routeParameters.keySet();
Map<String, String> publicRenderParameters =
FriendlyURLMapperThreadLocal.getPRPIdentifiers();
return routeParameterKeys.containsAll(publicRenderParameters.keySet());
}
/**
* Populates the parameter map using the parameters from the router and the
* default reserved parameters.
*
* @param parameterMap the parameter map to populate. This should be the map
* passed to {@link #populateParams(String, Map, Map)} by {@link
* com.liferay.portal.util.PortalImpl}.
* @param namespace the namespace to use for parameters added to
* <code>parameterMap</code>
* @param routeParameters the parameter map populated by the router
*/
protected void populateParams(
Map<String, String[]> parameterMap, String namespace,
Map<String, String> routeParameters) {
// Copy route parameters
for (Map.Entry<String, String> entry : routeParameters.entrySet()) {
addParameter(
namespace, parameterMap, entry.getKey(), entry.getValue());
}
// Copy default reserved parameters if they are not already set
for (Map.Entry<String, String> entry :
defaultReservedParameters.entrySet()) {
String key = entry.getKey();
if (!parameterMap.containsKey(key)) {
addParameter(namespace, parameterMap, key, entry.getValue());
}
}
}
protected Set<String> defaultIgnoredParameters;
protected Map<String, String> defaultReservedParameters;
private static final Log _log = LogFactoryUtil.getLog(
DefaultFriendlyURLMapper.class);
}