Adding Personalization (PZN) dynamic attribute using script (xml /.nodes)

Build and deployment of all portal artifacts can be automated using scripts, but recently we had small issue with automation of personalization rule that uses dynamic attribute (request) . I couldn't find sample xml (/PortalServer/doc/xml-samples/) or .nodes file to automate the creation of dynamic attribute (And also there no option to export from PZN Editor portlet) .

I couldn't find anything related to this on the infocenter also so thought post this here and this can be help if anybody looking on same lines.

To create the dynamic attribute manually
  1. select specific application object and click on manage properties



  1. Enter the name of the dynamic attribute and select type.


  1. you can select the dynamic attribute once it is created, but here there is no option to export/import this dynamic attribute.


To create the dynamic attribute using xml (.nodes)
After doing some research, was able to create below xml(saved it as .nodes file) that allowed me to create/import the dynamic attribute.

Filename : SivaTestRequestObjDynamicAttr.nodes

<?xml version="1.0" encoding="UTF-8"?>
<sv:node sv:name=".personalization/objects/ibmpersonalization:aoRequest/8448773582358480512"
    xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    xmlns:ibmpzn="http://www.ibm.com/software/portal/personalization/ibmpzn/1.0"
    xmlns:icm="http://www.ibm.com/software/data/cm/icm"
    xmlns:sv="http://www.jcp.org/jcr/sv/1.0"
    xmlns:ibmcontentwcm="http://content.ibm.com/wcm/1.0/"
    xmlns:remote="http://content.ibm.com/remote/1.0"
    xmlns:jcr="http://www.jcp.org/jcr/1.0"
    xmlns:ibmpersonalization="http://www.ibm.com/software/data/cm/personalization"
    xmlns:ibmpznnt="http://www.ibm.com/software/portal/personalization/ibmpznnt/1.0"
    xmlns:lotus="http://content.ibm.com/lotus/1.0"
    xmlns:ibmcmnt="http://www.ibm.com/software/portal/content/management/ibmcmnt/1.0"
    xmlns:icmdefault="http://www.ibm.com/software/data/cm/icmdefault"
    xmlns:pt="http://www.jcp.org/jcr/pt/1.0"
    xmlns:pzn="http://www.ibm.com/software/data/cm/pzn"
    xmlns:ibmcm="http://www.ibm.com/software/portal/content/management/ibmcm/1.0"
    xmlns:jcrnt="http://www.jcp.org/jcrnt/1.0">
<sv:property sv:name="jcr:nodeType" sv:type="pt:string">
<sv:value>ibmpersonalization:dynamicAttribute</sv:value>
</sv:property>
<sv:property sv:name="ibmpersonalization:propertyDisplayName" sv:type="pt:string">
<sv:value>testRequestObjDynamicAttr</sv:value>
</sv:property>
<sv:property sv:name="ibmpersonalization:propertyName" sv:type="pt:string">
<sv:value>testRequestObjDynamicAttr</sv:value>
</sv:property>
<sv:property sv:name="ibmpersonalization:propertyType" sv:type="pt:string">
<sv:value>java.lang.String</sv:value>
</sv:property>
<sv:property sv:name="ibmpersonalization:publishedOnDate" sv:type="pt:date">
</sv:property>
<sv:property sv:name="icm:created" sv:type="pt:date">
<sv:value>2014-05-09T23:18:23.788Z</sv:value>
</sv:property>
<sv:property sv:name="icm:lastModified" sv:type="pt:date">
<sv:value>2014-05-09T23:18:23.788Z</sv:value>
</sv:property>
</sv:node>


Couple of things are highlighted in above xml snippet

  1. Pointing to proper application object
.personalization/objects/ibmpersonalization:aoRequest/8448773582358480512

For Request ::::    ".personalization/objects/ibmpersonalization:aoRequest"
For Session  ::::    ".personalization/objects/ibmpersonalization:aoSession"

I think you can try same for other objects also ( aoWcmWebContent , aoRenderParameter… etc) but I didn't try remaining.

/8448773582358480512

This is random number (internal reference id) , if you want create multiple dynamic attributes have this different for each dynamic attribute.

  1. Property Display Name: Give dynamic attribute display name
  2. Property Name : Give dynamic attribute name
  3. Property Type : Specify the type of the attribute i.e. java.lang.String , java.lang.Boolean..etc


Now you can automate this by using PZNLoad (or manually import this .nodes file on different installations from PZN Editor portlet).


NOTE : Tested on WP8.0.0.1 / WP 8.0 and WP 7.0 versions and working fine. 

WebSphere Portal Cache - Page and Portlet Level Caching


The cache scope and cache expiry time are configured by page, portlet, and theme. WebSphere Portal combines this information to produce a final cache scope and expiry time for each page it serves.


Cache Settings
  1. Pages level
<content-node action="update" active="true" content-parentref="Z6_xxx" domain="rel" objectid="Z6_xxx" ordinal="100" type="label" uniquename="com.vakasiva.cachetest">
            <supported-markup markup="html" update="set"/>
            <localedata locale="en">
                <title>SivaCacheTest</title>
            </localedata>
            <parameter name="com.ibm.portal.IgnoreAccessControlInCaches" type="string" update="set"><![CDATA[false]]></parameter>
            <parameter name="com.ibm.portal.feed.remote-cache-expiry" type="string" update="set"><![CDATA[86400]]></parameter>
            <parameter name="com.ibm.portal.remote-cache-expiry" type="string" update="set"><![CDATA[86400]]></parameter>
           <parameter name="com.ibm.portal.remote-cache-scope" type="string" update="set"><![CDATA[NON-SHARED]]></parameter>

Scope Values are : SHARED/NON-SHARED
Expiry values are : 0 (never), -1(always) , number (specific sec)

NOTE:
By default, WebSphere® Portal does not permit shared caching for authenticated pages. You can use the Properties portlet or the XML configuration interface to override these default settings using the com.ibm.portal.IgnoreAccessControlInCaches parameter, but in most cases this is not recommended.


  1. Portlet level
    1. Portlet Definition

Possible settings at portlet definition level are
remote-cache-scope (SHARED/NON_SHARED)
EXPIRATION_CACHE (-1(never))
remote-cache-dynamic (true/false) : 

NOTE: The remote cache dynamic setting is an optimization to notify the container whether a portlet window can publish remote cache information at render time.


In portlet.xml

<portlet-app ... id="com.sivavaka.test.portletapp.cachetest">
<portlet>
<portlet-name>Cache Test</portlet-name>
<expiration-cache>0</expiration-cache>
<cache-scope>private</cache-scope>

In import-portlet.xml
<request ..>
    <portal action="locate">
        <web-app action="update" active="true" domain="rel" objectid="Z1_xxx" removable="true" uid="com.sivavaka.portletapp.cachetest.webmod">
            <url>file:///c:/tmp/CacheTest.war</url>
            <context-root>/wps/PA_CacheTest</context-root>
            <display-name>PA_CacheTest</display-name>
            <servlet action="update" active="true" domain="rel" name="Cache Test" objectid="ZV_xx" remote-cache-dynamic="false"/>
            <portlet-app action="update" active="true" defaultlocale="en" domain="rel" name="com.sivavaka.portletapp.cachetest" objectid="Z2_xx" uid="com.sivavaka.portletapp.cachetest">
                    <role actionset="User" update="set">
                        <mapping subjectid="all authenticated portal users" subjecttype="user_group" update="set"/>
                    </role>
                </access-control>
                       <portlet action="update" cache-expiration="0" active="true" defaultlocale="en" domain="rel" name="Report Links" objectid="Z3_xx" provided="false" servletref="ZV_xx">
                </portlet>
            </portlet-app>
        </web-app>
    </portal>
</request>

Or using the manage portlets



  1. Portlet Window (portlet instance level during rendering time)

String paramExpiry = "3000";
String paramScope = "SHARED";
renderResponse.setProperty( "portlet.remote-cache-scope", paramScope );
renderResponse.setProperty( RenderResponse.EXPIRATION_CACHE, paramExpiry )
//renderResponse.setProperty( RenderResponse.CACHE_SCOPE, paramExpiry )



  1. Portlets Wide (Across all portlets)

You can set the global settings in WP Navigator service provider
remote.cache.expiration (values : Number)
remoteCacheInfo.response.header.vary (values : List of HTTP header fields that can be put into the vary response header)


  1. Theme level

You can add following in theme import xml
<parameter update="set" name="com.ibm.portal.remote-cache-scope" type="string">SHARED</parameter>
<parameter update="set" name="com.ibm.portal.remote-cache-expiry" type="string">3000</parameter>


Cache Scopes
The cache scope determines where the content is cached.

There are two types of caching:
  1. Shared cache across users : 
A proxy server caches content and serves requests for the content. Not good for customized content. The following default values exist for portlet definitions and themes if nothing is provided:

Note: This type of caching should be used only for pages that contain public content that is not personalized.
  • Remote cache scope is non-shared
  • Remote cache expiry is 0 seconds

  1. Non-shared cache for a single user (Web browser cache):
The cache is typically located in each user's Web browser. Can be used for personalized content also . But if the computer is shared among multiple users, a user may see personalized content from other users if served from the browser cache. To prevent this from happening, do not enable private caching, even for personalize content.


Globabl Settings (In WP Navigator service)
  1. public.session
  2. public.expires
  3. remote.cache.expiration
  4. remoteCacheInfo.response.header.vary
  5. public.cache-control
  6. private.cache-control

Calculating how its cached
Multiple factors can affect the cache scope and expiry time for a page. The remote-cache-scope and remote-cache-expiration of a rendered page view is calculated as the minimum of the following factors:
  • Cache scope and expiry time specified for the page
  • Cache scope and expiry time of the portlets on the page
  • Cache scope and expiry time of the theme
  • Global values as defined in the Navigator Service 

NOTE: Ensure that the cache settings for all portlets on the page are consistent with the cache settings for the page. For example, if one portlet on a page is set to only be cached in a private browser cache, then the entire page can only be cached in a private browser cache, and performance is not optimal.


Reference:

WebSphere Portal URL types, setting base URL, Generating Relative and Friendly URL's


Different URL Formats In General
  1. Absolute URL
A complete URL containing the protocol, hostname, port, and path. For example: http://hostname.example.com:10039/wps/portal

  1. Server-relative URL
A URL that does not contain the protocol, hostname, and port, but does contain the full path part of the URL, starting with a slash. For example: "/wps/portal" .

  1. Relative URL
A URL that must be combined with a base URL in order to create a full URL. Relative to current request url.
For example browser url is http://host:port/wps/portal/firstpage generated markup that contains  <a href="testpage"/>testpage</a> then click "testpage" will try to open" http://host:port/wps/portal/testpage ".


Generating Relative and Friendly Portal URL's
Instead of encoding the complete navigational state into every URL on a page, WebSphere Portal provides ways for you to encode only the difference (delta) of the state that is represented by the URL with respect to the state of the request that generated the markup. The state of the current request is represented (in the HTML case) by the HTML <base/> tag in the head area of the HTML response. Overall, relative URLs are preferable because they are smaller and require less server resources to generate, giving better performance.

There are two ways to control the generation of relative URLs as opposed to server-relative URLs. 

1. Global (affects all URLs generated by WebSphere Portal)
2. AllowRelativeURL attribute on the <portal-navigation:urlGeneration/> tag, which overrides the global setting.

Approach 1 (Global):
1. Add/Update the following in statemanager service , "com.ibm.portal.state.accessors.url.URLContext.enableRelative"  with true/false for relative urls .

Approach 2 (Per URL based):
  1. Setting Base URL at theme level :

Execute the following using xmlaccess

<?xml version="1.0" encoding="UTF-8"?>
<request build="wpnext_372_01" type="update" version="7.0.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="PortalConfig_7.0.0.xsd">
     <portal action="locate">
          <theme action="update" uniquename="siva.custom.theme">
               <parameter name="com.ibm.portal.theme.hasBaseURL" type="string" update="set">true</parameter>
          </theme>
     </portal>
</request>

After successfull execution, it adds additional parameter to theme as below
<parameter name="com.ibm.portal.theme.hasBaseURL" type="string" update="set"><![CDATA[true]]></parameter>

  1. Add StatBase tag to theme
Add the following in the theme <head> markup
<portal-core:stateBase/>

Stores a base URL which will be used while resolving relative URL's, i.e it will generate the <base href=""> tag on the page like below. This enables shorter URLs and can improve the page serving performance


  1. When allowRelativeURL is true
While Generating the URL if you set the  allowrelativeURL to true , then generated url will be like 

<portal-navigation:urlGeneration allowRelativeURL="true" contentNode="com.sivavaka.test.pages.page1">
         <a href="<%wpsURL.write(escapeXmlWriter);%>">testlink</a>
</portal-navigation:urlGeneration>

output markup will be like below

  1. When allowRelativeURL is false
While Generating the URL if you set the allowRelativeURL to false , then generated url will be like below
<portal-navigation:urlGeneration allowRelativeURL="false" contentNode="com.sivavaka.test.pages.page1">
         <a href="<%wpsURL.write(escapeXmlWriter);%>">testlink</a>
</portal-navigation:urlGeneration>

output markup will be like below

<a href="/wps/myportal/Home/SivaTestPages/Page1/!ut/p/b1/pZDbCsIwDIafxRdYsqVdu8uidqvnI2pvZIqM4jZvRF_f6pWITsHw3yR8XyABC5uQSZ74kIQ12Dq_uCI_u1Odl_fextsIxz5DsTSYaTRaJj3D5n7EPbB5BlTaRzRhe06LDpHqxn_67D_fhL_57VRlTAwQ5SDlaFS2nCVTIlT0ze-BdbsquO6rAAMuSCIJRmHEUUQEK7CNC5C_Am8e2AywL8D9BQ-g6cYHgB9KIYyyU3WAypZaa3l0xk0K1WrdAEvr7Kw!/dl4/d5/L2dJQSEvUUt3QS80SmtFL1o2XzIwTzIwT003VUE3OTkwQU9NNFFVMDEwUzk2/">testlink</a>

  1. When KeepNavigationState is set
Regard less of the allowRelativeURL="true/false" , if you set the KeepNavigationState="false" , it will remove all navigation state and generate url like below
<portal-navigation:urlGeneration keepNavigationalState="false" contentNode="test.pages.page1">
         <a href="<%wpsURL.write(escapeXmlWriter);%>">testlink</a>
</portal-navigation:urlGeneration>

output markup will be like below
<a href="/wps/myportal/Home/TestPages/Page1">Testlink</a>

NOTE: 
1. If you don't enable the "hasBaseURL" at theme level, <portal-core:stateBase/> doesn't generate the <base href=""> tag causing the url looks like below even if you set the keepRelativeURL="true" i.e. it is equivalent to "allowRelativeURL=false" 

<a href="/wps/myportal/Home/TestPages/page1/!ut/p/b1/pZDbCsIwDIafxRdYsqVdu8uidqvnI2pvZIqM4jZvRF_f6pWITsHw3yR8XyABC5uQSZ74kIQ12Dq_uCI_u1Odl_fextsIxz5DsTSYaTRaJj3D5n7EPbB5BlTaRzRhe06LDpHqxn_67D_fhL_57VRlTAwQ5SDlaFS2nCVTIlT0ze-BdbsquO6rAAMuSCIJRmHEUUQEK7CNC5C_Am8e2AywL8D9BQ-g6cYHgB9KIYyyU3WAypZaa3l0xk0K1WrdAEvr7Kw!/dl4/d5/L2dJQSEvUUt3QS80SmtFL1o2XzIwTzIwT003VUE3OTkwQU9NNFFVMDEwUzk2/" >testlink</a>

References

DOJO Templates parsing is failed because of WebSeal Junction Cookie


PROBLEM: DOJO failed to load the page , i.e. failed to parse the template  

REASON: we have dojo templates name " sometemplate-name.html " and webseal junction that we use have junction cookie enabled (created junction with -j  option)

Webseal is adding following (junction cookie in the script block) to all .html pages, caused DOJO widget templates also got this script block added at end and failed to parse the template.
SCRIPT language="JavaScript">
<!-- 
document.cookie = "IV_JCT=%2Ftestjunct1; path=/";
//--> 
</SCRIPT>

Solution : Simplest solution we tried is rename the template file name to "sometemplate-name.txt"  (used ".txt" instead of the ".html")

WebSeal Junctions - URL Rewriting

Following article explains the different URL rewriting mechanisms in webseal junctions enabled environment. ( Extracted this information from white paper that I came across recently) 

Ideally, links in web pages protected by WebSEAL should be relative links. However, WebSEAL is usually deployed in situations where the back-end server is not under the control of the same group.

1.      Link Types
a.      Relative
b.      Server-Relative
c.      Absolute
2.      Outbound Links Modification
a.      Links in HTML
b.      Links in Script
3.      Inbound links Modification
1.      In-bound Server Relative Links
a.      Junction Cookies
b.      HTTP Referrer Header
c.      Transparent Path Junctions
d.      Junction Mapping Table
2.      In-bound Absolute Links and Virtual host junctions





Link Types

1.     Relative 
Relative links do not contain the name of the server or the name of the current directory. When the browser receives a relative link, the link appears to be located on the WebSEAL server. Relative links are correctly interpreted as links to other pages in the same directory on the same server.

For example, assume that this line appears in http://serverA/index.html:
<a href=”about.html”>About this site</a>

The browser retrieved this page from https://webseal/Junction1/index.html. This URL is correctly interpreted as pointing to https://webseal/Junction1/about.html. This request would go back to WebSEAL and WebSEAL would know to request http://serverA/about.html.

2.     Server-Relative
Server-relative links do not contain the name of the server, but they do contain the name of the directory.

For example, assume that this line appears in http://serverA/index.html:
<a href=”/contact.html”>Contact information</a>

The browser retrieved this page from https://webseal/Junction1/index.html. This URL is interpreted as pointing to /contact.html on the same server.However, from the browser’s perspective the server is WebSEAL. If WebSEAL did not change the HTML, the browser would attempt to retrieve https://webseal/contact.html instead of the correct URL, which is https://webseal/Junction1/contact.html.

3.     Absolute

Absolute links contain the name of the server and the directory.

For example, assume that this line appears in http://serverA/index.html:
<a href=”http://ServerA/copyright.html”>Copyright Information</a>

If WebSEAL did not change the HTML, the browser would attempt to connect directly to ServerA, bypassing WebSEAL. A correctly configured firewall would only allow connections to ServerA from WebSEAL.

Out-Bound Links Modification
server-relative and absolute links cannot work without changes

1.     Links in HTML
a.      Server-relative links are modified to include the current junction name.
b.      Absolute links might or might not need modification. Only links to WebSEAL protected resources must be modified. WebSEAL changes the links to protected resources into server-relative links (by default) and adds the proper junction name. If the links are for external sites, WebSEAL does not change them.

The following example shows a fragment of an HTML page from a back-end server:
<A HREF=”about.html”>About this site</A></BR>
<A HREF=”/contact.html”>Contact information</A></BR>
<A HREF=”http://ServerA/copyright.html”>Copyright information</A></BR>
<A HREF=”http://www.ibm.com”>IBM’s Web site</A></BR>

WebSEAL changes the links so that the browser receives this version:
<A HREF=”about.html”>About this site</A></BR>
<A HREF=”/Junction1/contact.html”>Contact information</A></BR>
<A HREF=”/Junction1/copyright.html”>Copyright information</A></BR>
<A HREF=”http://www.ibm.com”>IBM’s Web site</A></BR>

2.     Links in Script
This works only for absolute URLs (http[s]://<host name>/<path>  , where the host name is a server in a junction) , but it might not work in every case. Consider the following HTML coming from the back end:

<SCRIPT LANGUAGE=”JavaScript”>
<!--
document.write(“<A HREF=/bad.html>This will fail</A></BR>”);
var path = “ServerA/bad.html”;
document.write(“<A HREF=http://” + path + “>Link</A></BR>”);
document.write(“Go to http://ServerA/fun.html</BR>”);
// -->
</SCRIPT>

WebSEAL will modify this HTML and send the following HTML to the browser:

<SCRIPT LANGUAGE=”JavaScript”>
<!--
document.write(“<A HREF=/bad.html>This will fail</A></BR>”);
var path = “ServerA/bad.html”;
document.write(“<A HREF=http://” + path + “>Link</A></BR>”);
document.write(“Go to /Junction1/fun.html</BR>”);
// -->
</SCRIPT>
The first link will not be modified because it is server-relative. The second link, http://ServerA/bad.html, will also not be modified because WebSEAL will not be able to identify that it is a link. The string http://ServerA/fun.html will be modified even though it is not a link.


NOTE: Enabling script filtering

1. Modify the WebSEAL instance configuration file. The [script-filtering] stanza must contain this line:
script-filter = yes
2. Restart WebSEAL:
pdweb restart
3. Create a junction with the junction cookie enabled (-j from the command line).

  
In-Bound Links Modification
1.     In-bound Server Relative links
a.     Junction cookie
If a junction is created with the -j option (enable junction cookie), WebSEAL adds JavaScript to every HTML page to include a cookie that contains the junction. When the browser requests another page from the same server, it sends back the cookie with the HTTP request

The HTML source that WebSEAL sends to the browser starts with code such as:
<SCRIPT language=”JavaScript”>
<!--
document.cookie = “IV_JCT=%2FJunction2; path=/”;
//-->
</SCRIPT>

Using JavaScript, this code segment specifies that the cookie IV_JCT will be sent with any request for a page on this server that starts with a slash (/). This method fails in some cases. For example:
a.      If you keep a local copy of the page and click a link after the cookie expires,WebSEAL cannot direct the request. A different window or tab could overwrite the cookie if you perform the following steps:

1.      Open a page using a junction that has junction cookies enabled, The junction cookie is set to Jct1.
https://<webseal>/Jct1/index.html

2.      In the same browser, open another window for a different junction on the same WebSEAL server, which also has junction cookies enabled, The junction cookie is set to Jct2.
https://<webseal>/Jct2/page1.html

3.      When you return to the original window and click a link, for example to /page2.html, the cookie is set to Jct2. WebSEAL will attempt to retrieve /page2.html from the server for that junction, instead of Jct1.

b.      WebSEAL adds JavaScript to any page that the back-end server reports to be oftype text/html. If the back-end server erroneously reports as HTML pages that are not HTML, WebSEAL adds JavaScript where it is not appropriate.

b.     HTTP referrer header
Referer headers rely on the browser to send them out. Browsers do not always send referer
headers.
Sample Request
GET /page2.html HTTP/1.1
.
.
host: webseal

because the browser sent the referer header to WebSEAL, WebSEAL interprets the request as https://webseal/
Junction1/page2.html and directs it to the correct back-end server

c.      Transparent Path junctions
A transparent path junction has the same name as a directory on the back-end server. If different Web servers use different directories, you can use those directories as junctions. WebSEAL does not change directory names in this scenario, server-relative links require no modification


Use the -x option to create the junctions:
s t <webseal server> create -t tcp -h backend1 -x /app1
s t <webseal server> create -t tcp -h backend2 -x /app2


d.     Junction Mapping Table
The junction mapping table is a text file that contains junctions and regular expressions. When WebSEAL looks for a junction, it tries to find which regular expression in the table matches.
Sample

/win *.asp
/win *.htm
/junction1 /wps/myportal/*

The junction mapping table is located in /opt/pdweb/www-default/lib/jmt.conf by default. This file name is specified in the instance configuration file and can be modified as needed.
After you modify the junction mapping table, issue the following pdadmin command:
s t <webseal server> jmt load

2.     In-bound absolute links and virtual host junctions
These are junctions that WebSEAL identifies using the host: HTTP header, instead of using a directory name. With virtual host junctions, multiple host names (for example, www.brand1.com and www.brand2.com) resolve to the IP address for WebSEAL



When WebSEAL receives the request, the HTTP header contains a host: field that corresponds to the host part of the URL. For example, if the browser tried to retrieve
https://www.brand1.com/page1.html, the HTTP request would look like the following
example:
GET /page1.html HTTP/1.1
...
With this method, WebSEAL can receive absolute links and then deal with them correctly

References: