/**
* Licensed to Apereo under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright ownership. Apereo
* 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 the
* following location:
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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.apereo.portal.rendering;
import java.util.Map;
import javax.portlet.WindowState;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.apereo.portal.layout.IUserLayoutManager;
import org.apereo.portal.layout.om.IStylesheetDescriptor;
import org.apereo.portal.layout.om.IStylesheetParameterDescriptor;
import org.apereo.portal.portlet.PortletUtils;
import org.apereo.portal.portlet.om.IPortletWindow;
import org.apereo.portal.portlet.registry.IPortletWindowRegistry;
import org.apereo.portal.portlet.rendering.IPortletRenderer;
import org.apereo.portal.url.IPortalRequestInfo;
import org.apereo.portal.url.IUrlSyntaxProvider;
import org.apereo.portal.utils.Tuple;
import org.apereo.portal.utils.cache.CacheKey;
import org.apereo.portal.xml.stream.FilteringXMLEventReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Enforces a specific WindowState based on the "dashboardForcedWindowState" {@link
* IStylesheetDescriptor} parameter. For renderings where a specific portlet is not specified and
* the stylesheet descriptor specifies a dashboardForcedWindowState then all portlets in the
* pipeline will have their window state set to the specified value. If the IStylesheetDescriptor is
* not specified a check is made to ensure the window states are not {@link WindowState#MAXIMIZED},
* {@link IPortletRenderer#DETACHED}, or {@link IPortletRenderer#EXCLUSIVE}
*
*/
public class WindowStateSettingsStAXComponent extends StAXPipelineComponentWrapper {
protected final Logger logger = LoggerFactory.getLogger(getClass());
private IUrlSyntaxProvider urlSyntaxProvider;
private IPortletWindowRegistry portletWindowRegistry;
private StylesheetAttributeSource stylesheetAttributeSource;
@Autowired
public void setPortletWindowRegistry(IPortletWindowRegistry portletWindowRegistry) {
this.portletWindowRegistry = portletWindowRegistry;
}
@Autowired
public void setUrlSyntaxProvider(IUrlSyntaxProvider urlSyntaxProvider) {
this.urlSyntaxProvider = urlSyntaxProvider;
}
public void setStylesheetAttributeSource(StylesheetAttributeSource stylesheetAttributeSource) {
this.stylesheetAttributeSource = stylesheetAttributeSource;
}
/* (non-Javadoc)
* @see org.apereo.portal.rendering.PipelineComponent#getCacheKey(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public CacheKey getCacheKey(HttpServletRequest request, HttpServletResponse response) {
//Initiating rendering of portlets will change the stream at all
return this.wrappedComponent.getCacheKey(request, response);
}
/* (non-Javadoc)
* @see org.apereo.portal.rendering.PipelineComponent#getEventReader(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public PipelineEventReader<XMLEventReader, XMLEvent> getEventReader(
HttpServletRequest request, HttpServletResponse response) {
final PipelineEventReader<XMLEventReader, XMLEvent> pipelineEventReader =
this.wrappedComponent.getEventReader(request, response);
final XMLEventReader eventReader = pipelineEventReader.getEventReader();
final IStylesheetDescriptor stylesheetDescriptor =
stylesheetAttributeSource.getStylesheetDescriptor(request);
final IPortalRequestInfo portalRequestInfo =
this.urlSyntaxProvider.getPortalRequestInfo(request);
final XMLEventReader filteredEventReader;
if (portalRequestInfo.getTargetedPortletWindowId() == null) {
final IStylesheetParameterDescriptor defaultWindowStateParam =
stylesheetDescriptor.getStylesheetParameterDescriptor(
"dashboardForcedWindowState");
if (defaultWindowStateParam != null) {
//Set all window states to the specified default
final WindowState windowState =
PortletUtils.getWindowState(defaultWindowStateParam.getDefaultValue());
filteredEventReader =
new SinglePortletWindowStateSettingXMLEventReader(
request, eventReader, windowState);
} else {
//Make sure there aren't any portlets in a "targeted" window state
filteredEventReader =
new NonTargetedPortletWindowStateSettingXMLEventReader(
request, eventReader);
}
} else {
//Not mobile, don't bother filtering
filteredEventReader = eventReader;
}
final Map<String, String> outputProperties = pipelineEventReader.getOutputProperties();
return new PipelineEventReaderImpl<XMLEventReader, XMLEvent>(
filteredEventReader, outputProperties);
}
private abstract class PortletWindowStateSettingXMLEventReader extends FilteringXMLEventReader {
private final HttpServletRequest request;
public PortletWindowStateSettingXMLEventReader(
HttpServletRequest request, XMLEventReader reader) {
super(reader);
this.request = request;
}
@Override
protected final XMLEvent filterEvent(XMLEvent event, boolean peek) {
if (event.isStartElement()) {
final StartElement startElement = event.asStartElement();
final QName name = startElement.getName();
final String localName = name.getLocalPart();
if (IUserLayoutManager.CHANNEL.equals(localName)
|| IUserLayoutManager.CHANNEL_HEADER.equals(localName)) {
final Tuple<IPortletWindow, StartElement> portletWindowAndElement =
portletWindowRegistry.getPortletWindow(request, startElement);
if (portletWindowAndElement == null) {
logger.warn("No portlet window found for: {}", localName);
return event;
}
final IPortletWindow portletWindow = portletWindowAndElement.first;
final WindowState windowState = getWindowState(portletWindow);
//Set the portlet's state, skip if the state is already correct
final WindowState currentWindowState = portletWindow.getWindowState();
if (!windowState.equals(currentWindowState)) {
portletWindow.setWindowState(windowState);
if (logger.isDebugEnabled()) {
logger.debug(
"Changing WindowState from {} to {} for {}",
new Object[] {currentWindowState, windowState, portletWindow});
}
portletWindowRegistry.storePortletWindow(request, portletWindow);
}
return portletWindowAndElement.second;
}
}
return event;
}
protected abstract WindowState getWindowState(IPortletWindow portletWindow);
}
private class SinglePortletWindowStateSettingXMLEventReader
extends PortletWindowStateSettingXMLEventReader {
private final WindowState state;
public SinglePortletWindowStateSettingXMLEventReader(
HttpServletRequest request, XMLEventReader reader, WindowState state) {
super(request, reader);
this.state = state;
}
@Override
protected WindowState getWindowState(IPortletWindow portletWindow) {
return state;
}
}
private class NonTargetedPortletWindowStateSettingXMLEventReader
extends PortletWindowStateSettingXMLEventReader {
public NonTargetedPortletWindowStateSettingXMLEventReader(
HttpServletRequest request, XMLEventReader reader) {
super(request, reader);
}
@Override
protected WindowState getWindowState(IPortletWindow portletWindow) {
final WindowState windowState = portletWindow.getWindowState();
if (windowState != null && PortletUtils.isTargetedWindowState(windowState)) {
return WindowState.NORMAL;
}
return windowState;
}
}
}