Rendering Attributes

Purpose

Rendering attribute subsystem allows to decorate XML streams going through a rendering pipeline (i.e. User Layout XML and its further transfromations) with attributes. The subsystem provides classes and interfaces necessary to define, inject, maintain and persist rendering attributes. A specific configuraiton of rendering attributes is used to implement uP2-style structure- and theme-transformation XSL params, element and folder attributes.

Params and Attributes

Params are special types of rendering attributes that are not associatiated with any particular element

Main interfaces and implementations

Providers

Rendering Attribute Providers serve as overall points of read- and write- access to attributes by the rest of the framework (see UPC:UML diagram 1).
A rendering attribute provider implementation (see RenderingAttributeProviderImpl) is typically configured with

  • a description of rendering attributes (IRenderingAttributeDescription). Containing information such as default values or persistence scope for each attribute.
  • an element resolver (IElementResolver) which is tasked with recognizing the elements with which defined attributes are associated (such as "folder" elements)
  • an object containing rendering attribute values (IRenderingAttributeValues)


Multiple rendering attribute providers can be organized into lists and layers using the implementations of the IHierarchicalRenderingAttributeProvider. For instance, two different providers, one configured to provide "folder" attributes and another "channel" attributes can be combined using ListRenderingAttributeProvider. HierarchicalRenderingAttributeProvider allows to override attribute values supplied by the parent provider. For example, with distributed/aggregated layout managers values of the "folder" attributes should default to those defined for the fragment. This can be achieved by configuring two providers, one recognizing folders by regular layoutId, another recognizing folders by something like "fragmentNodeId". The latter would be configured as a parent provider.

Values

An implementation of the IRenderingAttributeValues (see UPC:UML diagram 2) can be configured using LayeredRenderingAttributeValues to separate persistent and runtime attributes into different layers. PersistentRenderingAttributes implementation is configured with DAO implementation and persistence controller.

Incorporating rendering attributes into XML streams

Insertion of of the rendering attribute values into the rendering pipeline, is governed by the rendering attribute injectors (see UML diagram 3). Currently, there are two two implementations: AttributeRenderingAttributeInjector inserts values as XML element attributes, and can be configured to perform injection that's bacwards compatible with uPortal2; ElementRenderingAttributeInjector injects the values in a series of child XML elements. To support XSL params, the interface provides a prepareTransformer() method that allows the injector to set XSL param values if necessary.

Configuration

Below we provide examples of how to configure rendering attribute subsystem to mimic functionality of uPortal2 transformation (in this case theme transform). The first example configures everything explicitly, from scratch. The second example uses uP2-specific helper classes.

General

Providers can be generally declared in a static scope, and values are usually associated with a particular session. In the example below, sessionContext is a child bean factory that contains declaration of the attribute value beans.
Here is an example declaration of the rendering attributes and providers for the tabcolumn transformation.

<!-- Description of params and attributes --> 
<bean id="tabcolumn.attributeDescription" class="org.jasig.portal.rendering.attributes.RenderingAttributesDescriptionImpl">
    <property name="parameterDescriptions">
        <set>
            <bean id="tabcolumn.activeTab" class="org.jasig.portal.rendering.attributes.AttributeDescriptionImpl">
            	<property name="name" value="activeTab"/>
                <property name="defaultValue" value="1"/>
                <property name="description" value="active tab number"/>
                <property name="persistenceScope">
                	<ref bean="sessionAttributePersistenceScope"/>
                </property>
            </bean>
        </set>
    </property>
    <property name="attributeDescriptions">
        <set>
            <bean id="tabcolumn.width" class="org.jasig.portal.rendering.attributes.AttributeDescriptionImpl">
                <property name="name" value="width"/>
                <property name="description"><value>column width</value></property>
                <property name="defaultValue" value="100"/>
                <property name="persistenceScope">
                	<ref bean="sessionAttributePersistenceScope"/>
                </property>
            </bean>
        </set>
    </property>
</bean>

<!-- attribute provider -->
<bean id="tabcolumn.attributeProvider" class="org.jasig.portal.rendering.attributes.RenderingAttributeProviderImpl">
    <property name="providerName" value="tabcolumn.folder"/>
    <property name="renderingAttributesDescription">
        <ref bean="tabcolumn.attributeDescription"/>
    </property>
    <property name="elementResolver">
        <ref bean="folderElementResolver"/>
    </property>
    <property name="renderingAttributeValues">
        <ref bean="sessionScope/tabcolumn.attributeValues"/>
    </property>
</bean>

<!-- stylesheet description -->
<bean id="tabcolumn.stylesheetDescription" class="org.jasig.portal.rendering.sax.xslt.StylesheetDescriptionImpl">
    <property name="stylesheetLocation">
        <value>/uP2/org/jasig/portal/layout/AL_TabColumn/AL_TabColumn.xsl</value>
    </property>
</bean>

<!--=== HELPER BEANS ===-->

<!-- folder element resolver - picks up folders and constructs a key based on the ID attribute -->
<bean id="folderElementResolver" class="org.jasig.portal.rendering.attributes.ElementResolverImpl">
    <property name="elementNames">
        <list>
            <value>folder</value>
        </list>
    </property>
    <property name="attributeNames">
        <list>
            <value>ID</value>
        </list>
    </property>
    <property name="elementResolverKeyFactory">
        <ref bean="resolverKeyFactory"/>
    </property>
</bean>

<!-- session attribute persistence scope  -->
<bean name="sessionAttributePersistenceScope" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
    <property name="staticField">
        <value>org.jasig.portal.rendering.attributes.AttributePersistenceScope.sessionScope</value>
    </property>
</bean>

<!-- persistent attribute scope -->
<bean name="persistentAttributePersistenceScope" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
    <property name="staticField">
        <value>org.jasig.portal.rendering.attributes.AttributePersistenceScope.permanentScope</value>
    </property>
</bean>

<!-- recognizes all non-persistent attributes -->
<bean id="tabcolumn.sessionAttributeLevelResolver"  class="org.jasig.portal.rendering.attributes.SessionAttributeLevelResolver">
    <property name="renderingAttributesDescription">
        <ref bean="tabcolumn.attributeDescription"/>
    </property>
</bean>

It is complemented with the sessionScope factory containing declaration of the attribute values (sessionScope/tabcolumn.attributeValues)

<!-- The following values are layered. Top layer is responsible for all non-persistent values,
     and the parent layer handles persistent values. It references controllers from uP2 context.
-->
<bean id="nestedTables.attributeValues" class="org.jasig.portal.rendering.attributes.LayeredRenderingAttributeValues">
    <!-- specify resolver that determines which attributes stay at this level,
         and which go to the parent level
    -->
    <property name="attributeLevelResolver">
        <ref bean="../tabcolumn.sessionAttributeLevelResolver"/>
    </property>
    <property name="parentAttributeValues">
        <!-- persistent attribute values configuration -->
        <bean id="nestedTables.persistentAttributeValues" class="org.jasig.portal.rendering.attributes.PersistentRenderingAttributeValues">
            <property name="renderingAttributesDao">
                <bean id="userRenderingAttributesDao" class="org.jasig.portal.rendering.attributes.dao.UserRenderingAttributeValuesDao">
                    <property name="userRenderingAttributesDao">
                        <ref bean="/persistence/userRenderingAttributesDaoImpl"/>
                    </property>
                    <property name="userController">
                        <ref bean="/uP2Context/userController"/>
                    </property>
                    <property name="stylesheetName" value="tab column"/>
                </bean>
            </property>
            <property name="persistenceController">
                <ref bean="/uP2Context/persistenceController"/>
            </property>
        </bean>
    </property>
</bean>

uPortal2 legacy helpers

There are two major helpers for supporting uP2:
1. PersistentLegacyStylesheetDescription allows to construct stylesheet description instance from existing SDF files
2. LegacyRenderingAttributeProviderFactoryBean allows to construct complete attribute provider configuration based on a description and attribute values beans
The following example configures uP2 nested tables theme:

<!-- description configured using PersistentLegacyStylesheetDescription -->
<bean id="nestedTables.legacyStylesheetDescription" class="org.jasig.portal.rendering.sax.xslt.legacy.persistance.PersistentLegacyStylesheetDescription">
    <property name="legacyStylesheetDescriptionDao">
        <ref bean="legacyStylesheetDescriptionDao"/>
    </property>
    <property name="stylesheetDescriptionLocation">
        <value>/uP2/org/jasig/portal/layout/AL_TabColumn/integratedModes/integratedModes.sdf</value>
    </property>
    <property name="stylesheetLocation">
        <value>/uP2/org/jasig/portal/layout/AL_TabColumn/integratedModes/integratedModes.xsl</value>
    </property>
</bean>

<!-- provider configured using LegacyRenderingAttributeProviderFactoryBean helper -->
<bean id="nestedTables.attributeProvider" class="org.jasig.portal.rendering.sax.xslt.legacy.spring.LegacyRenderingAttributeProviderFactoryBean">
    <property name="providerName" value="theme"/>
    <property name="legacyStylesheetDescription">
        <ref bean="nestedTables.legacyStylesheetDescription"/>
    </property>
    <property name="layeredRenderingAttributeValues">
        <ref bean="sessionScope/nestedTables.attributeValues"/>
    </property>
</bean>

The provider for standard uP2 rendering attribute scheme has to be layered. Two child providers should be created, one responsible for handling channel attributes, and another for folder attributes. This is becase a given provider operates on one set of attribute descriptions, and different attributes may appear for channels and folders. In the example above, LegacyRenderingAttributeProviderFactoryBean created a provider that could be configured manually like this:

<bean id="nestedTables.attributeProvider" class="org.jasig.portal.rendering.attributes.HierarchicalRenderingAttributeProvider">
    <property name="providerName" value="structure"/>
    <property name="renderingAttributesDescription">
        <bean id="nestedTables.legacyParamDescriptions" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
            <property name="targetObject"><ref bean="nestedTables.legacyStylesheetDescription"/></property>
            <property name="targetMethod"><value>getParamsDescription</value></property>
        </bean>
    </property>
    <property name="renderingAttributeValues">
        <ref bean="sessionScope/nestedTables.attributeValues"/>
    </property>
    <property name="propagateSetAttributesCommands" value="true"/>
    <property name="propagateDeleteCommands" value="true"/>
    
    <property name="parentProviders">
        <list>
            <!-- folder attribute provider -->
            <bean id="nestedTables.folderAttributeProvider" class="org.jasig.portal.rendering.attributes.RenderingAttributeProviderImpl">
                <property name="providerName" value="nestedTables.folder"/>
                <property name="renderingAttributesDescription">
                    <bean id="nestedTables.legacyFolderAttributeDescriptions" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
                        <property name="targetObject"><ref bean="nestedTables.legacyStylesheetDescription"/></property>
                        <property name="targetMethod"><value>getFolderAttributesDescription</value></property>
                    </bean>
                </property>
                <property name="elementResolver">
                    <ref bean="folderElementResolver"/>
                </property>
                <property name="renderingAttributeValues">
                    <ref bean="sessionScope/nestedTables.attributeValues"/>
                </property>
            </bean>
            <!-- channel attribute provider -->
            <bean id="nestedTables.channelAttributeProvider" class="org.jasig.portal.rendering.attributes.RenderingAttributeProviderImpl">
                <property name="providerName" value="nestedTables.folder"/>
                <property name="renderingAttributesDescription">
                    <bean id="nestedTables.legacyChannelAttributeDescriptions" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
                        <property name="targetObject"><ref bean="nestedTables.legacyStylesheetDescription"/></property>
                        <property name="targetMethod"><value>getChannelAttributesDescription</value></property>
                    </bean>
                </property>
                <property name="elementResolver">
                    <ref bean="channelElementResolver"/>
                </property>
                <property name="renderingAttributeValues">
                    <ref bean="sessionScope/nestedTables.attributeValues"/>
                </property>
            </bean>
        </list>
    </property>
</bean>