/** * 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.layout; import java.util.Collection; import java.util.Deque; import java.util.LinkedList; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventFactory; import javax.xml.stream.XMLEventReader; import javax.xml.stream.events.Attribute; import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import org.apereo.portal.portlet.om.IPortletDefinition; import org.apereo.portal.portlet.om.IPortletDefinitionParameter; import org.apereo.portal.xml.stream.InjectingXMLEventReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * XMLEventReader that can return dynamically created content to add into the document. * */ public class TransientUserLayoutXMLEventReader extends InjectingXMLEventReader { private static final XMLEventFactory EVENT_FACTORY = XMLEventFactory.newFactory(); protected final Logger logger = LoggerFactory.getLogger(this.getClass()); private final TransientUserLayoutManagerWrapper userLayoutManager; private final String rootFolderId; public TransientUserLayoutXMLEventReader( TransientUserLayoutManagerWrapper userLayoutManager, XMLEventReader wrappedReader) { super(wrappedReader); this.userLayoutManager = userLayoutManager; this.rootFolderId = this.userLayoutManager.getRootFolderId(); } /** * Examines the current token and when appropriate creates and returns dynamically created * content. If dynamic content is not created, return null. * * @param event The current event * @return Dynamic content to inject into document, else null if no additional dynamic content * was created. */ @Override protected Deque<XMLEvent> getAdditionalEvents(XMLEvent event) { if (event.isStartElement()) { final StartElement startElement = event.asStartElement(); //All following logic requires an ID attribute, ignore any element without one final Attribute idAttribute = startElement.getAttributeByName(IUserLayoutManager.ID_ATTR_NAME); if (idAttribute == null) { return null; } // Create and return a transient (dynamically created) folder that includes a transient channel // if we are processing the tart element of the root node // iff the subscribeId is present and describes a transient channel and not a regular layout channel. final String subscribeId = this.userLayoutManager.getFocusedId(); if (this.rootFolderId.equals(idAttribute.getValue()) && subscribeId != null && !subscribeId.equals("") && this.userLayoutManager.isTransientChannel(subscribeId)) { IPortletDefinition chanDef = null; try { chanDef = this.userLayoutManager.getChannelDefinition(subscribeId); } catch (Exception e) { logger.error( "Could not obtain IChannelDefinition for subscribe id: {}", subscribeId, e); } if (chanDef != null) { final QName name = startElement.getName(); final String namespaceURI = name.getNamespaceURI(); final String prefix = name.getPrefix(); final Deque<XMLEvent> transientEventBuffer = new LinkedList<XMLEvent>(); final Collection<Attribute> transientFolderAttributes = new LinkedList<Attribute>(); transientFolderAttributes.add( EVENT_FACTORY.createAttribute( "ID", TransientUserLayoutManagerWrapper.TRANSIENT_FOLDER_ID)); transientFolderAttributes.add( EVENT_FACTORY.createAttribute( "name", chanDef != null ? chanDef.getTitle() : "Temporary")); transientFolderAttributes.add(EVENT_FACTORY.createAttribute("type", "regular")); transientFolderAttributes.add(EVENT_FACTORY.createAttribute("hidden", "false")); transientFolderAttributes.add( EVENT_FACTORY.createAttribute("unremovable", "true")); transientFolderAttributes.add( EVENT_FACTORY.createAttribute("immutable", "true")); transientFolderAttributes.add( EVENT_FACTORY.createAttribute("unremovable", "true")); transientFolderAttributes.add( EVENT_FACTORY.createAttribute("dlm:addChildAllowed", "false")); transientFolderAttributes.add( EVENT_FACTORY.createAttribute("dlm:deleteAllowed", "false")); transientFolderAttributes.add( EVENT_FACTORY.createAttribute("dlm:editAllowed", "false")); transientFolderAttributes.add( EVENT_FACTORY.createAttribute("dlm:moveAllowed", "false")); transientFolderAttributes.add( EVENT_FACTORY.createAttribute("dlm:precedence", "100.0")); transientFolderAttributes.add( EVENT_FACTORY.createAttribute("transient", "true")); final StartElement transientFolder = EVENT_FACTORY.createStartElement( prefix, namespaceURI, IUserLayoutManager.FOLDER, transientFolderAttributes.iterator(), null); transientEventBuffer.add(transientFolder); //TODO Move IChannelDefinition/IPortletDefinition -> StAX events code somewhere reusable final Collection<Attribute> channelAttrs = new LinkedList<Attribute>(); channelAttrs.add(EVENT_FACTORY.createAttribute("ID", subscribeId)); channelAttrs.add( EVENT_FACTORY.createAttribute( "typeID", Integer.toString(chanDef.getType().getId()))); channelAttrs.add(EVENT_FACTORY.createAttribute("hidden", "false")); channelAttrs.add(EVENT_FACTORY.createAttribute("unremovable", "true")); channelAttrs.add(EVENT_FACTORY.createAttribute("dlm:deleteAllowed", "false")); channelAttrs.add(EVENT_FACTORY.createAttribute("dlm:moveAllowed", "false")); channelAttrs.add(EVENT_FACTORY.createAttribute("name", chanDef.getName())); channelAttrs.add( EVENT_FACTORY.createAttribute("description", chanDef.getDescription())); channelAttrs.add(EVENT_FACTORY.createAttribute("title", chanDef.getTitle())); channelAttrs.add( EVENT_FACTORY.createAttribute( "chanID", chanDef.getPortletDefinitionId().getStringId())); channelAttrs.add(EVENT_FACTORY.createAttribute("fname", chanDef.getFName())); channelAttrs.add( EVENT_FACTORY.createAttribute( "timeout", Integer.toString(chanDef.getTimeout()))); channelAttrs.add(EVENT_FACTORY.createAttribute("transient", "true")); final StartElement startChannel = EVENT_FACTORY.createStartElement( prefix, namespaceURI, IUserLayoutManager.CHANNEL, channelAttrs.iterator(), null); transientEventBuffer.offer(startChannel); // add channel parameter elements for (final IPortletDefinitionParameter parm : chanDef.getParameters()) { final Collection<Attribute> parameterAttrs = new LinkedList<Attribute>(); parameterAttrs.add(EVENT_FACTORY.createAttribute("name", parm.getName())); parameterAttrs.add(EVENT_FACTORY.createAttribute("value", parm.getValue())); final StartElement startParameter = EVENT_FACTORY.createStartElement( prefix, namespaceURI, IUserLayoutManager.PARAMETER, parameterAttrs.iterator(), null); transientEventBuffer.offer(startParameter); final EndElement endParameter = EVENT_FACTORY.createEndElement( prefix, namespaceURI, IUserLayoutManager.PARAMETER, null); transientEventBuffer.offer(endParameter); } final EndElement endChannel = EVENT_FACTORY.createEndElement( prefix, namespaceURI, IUserLayoutManager.CHANNEL, null); transientEventBuffer.offer(endChannel); final EndElement endFolder = EVENT_FACTORY.createEndElement( prefix, namespaceURI, IUserLayoutManager.FOLDER, null); transientEventBuffer.offer(endFolder); return transientEventBuffer; } else { // I don't think subscribeId could be null, but log warning if so. logger.warn( "Unable to resolve portlet definition for subscribe ID {}", subscribeId); } } } return null; } @Override protected XMLEvent getPeekEvent(XMLEvent event) { //Not the most efficient way of doing this since the whole deque is built but we only need the first element. final Deque<XMLEvent> additionalEvents = this.getAdditionalEvents(event); if (additionalEvents != null) { return additionalEvents.pop(); } return null; } }