Tag Archives: WebSphere Portal 8.5

Where is WebSphere Portal Jars?

Below is the jars file location for WebSphere Portal 8.5 SPI.

ilwwcm-api.jar 
Reside at <PortalServer>/wcm/prereq.wcm/wcm/shared/app/ilwwcm-api.jar
It is responsible for the following classes:

  • com.ibm.workplace.wcm.api.*

wp.model.api.jar 
Reside at <PortalServer>/base/wp.model.api/shared/app/wp.model.api.jar
It is responsible for the following classes:

  • com.ibm.portal.state.accessors.pagemode.*

wp.resolver.friendly.api.jar
Reside at <PortalServer>/base/wp.resolver/wp.resolver.friendly.api/shared/app/wp.resolver.friendly.api.jar
It is responsible for the following classes:

  • com.ibm.portal.resolver.friendly.*

wp.services.api.jar
Reside at <PortalServer>/base/wp.services.api/shared/app/wp.services.api.jar
It is responsible for the following classes:

  • com.ibm.portal.services.project.*

Just drop us a comment if you need to find out the jar location of any WebSphere Portal classes.

WebSphere Portal EJPPD0015E: Portlet application manager failed

Scenario

You have created your portlet using RAD and deployed the portlet to WebSphere Portal. For some reasons, you need to remove the portlet (not update) and re-deployed the portlet again. Immediately you encountered “EJPPD0015E: Portlet application manager failed” exception in your SystemOut.log file.

Then you begin to trace and discover that the exception occurs at EMF2DOMAdapterImpl_ERROR_0:

Caused by: java.lang.IllegalStateException: EMF2DOMAdapterImpl_ERROR_0
at org.eclipse.wst.common.internal.emf.resource.EMF2DOMAdapterImpl.handleInvalidMultiNodes(EMF2DOMAdapterImpl.java:1402)
at org.eclipse.wst.common.internal.emf.resource.EMF2DOMAdapterImpl.findDOMNode(EMF2DOMAdapterImpl.java:1389)
at org.eclipse.wst.common.internal.emf.resource.EMF2DOMAdapterImpl.primUpdateMOFFeature(EMF2DOMAdapterImpl.java:1572)
at org.eclipse.wst.common.internal.emf.resource.EMF2DOMAdapterImpl.updateMOFFeature(EMF2DOMAdapterImpl.java:1992)
at org.eclipse.wst.common.internal.emf.resource.EMF2DOMAdapterImpl.primUpdateMOF(EMF2DOMAdapterImpl.java:1010)
at org.eclipse.wst.common.internal.emf.resource.EMF2DOMAdapterImpl.updateMOF(EMF2DOMAdapterImpl.java:986)
at org.eclipse.wst.common.internal.emf.resource.EMF2DOMAdapterImpl.primUpdateMOFMultiFeature(EMF2DOMAdapterImpl.java:489)
at org.eclipse.wst.common.internal.emf.resource.EMF2DOMAdapterImpl.updateMOFRootFeature(EMF2DOMAdapterImpl.java:1039)
at org.eclipse.wst.common.internal.emf.resource.EMF2DOMAdapterImpl.primUpdateMOF(EMF2DOMAdapterImpl.java:1006)
at org.eclipse.wst.common.internal.emf.resource.EMF2DOMAdapterImpl.updateMOF(EMF2DOMAdapterImpl.java:986)
at org.eclipse.wst.common.internal.emf.resource.EMF2DOMRenderer.doLoad(EMF2DOMRenderer.java:331)
at org.eclipse.wst.common.internal.emf.resource.TranslatorResourceImpl.basicDoLoad(TranslatorResourceImpl.java:633)
... 78 more

If that is the reason, then you might just be in luck. Go to your WEB-INF/web.xml and ensure that the web.xml file does not contains multiple nodes of the following:

  • session-config
  • welcome-file-list
  • jsp-config
  • login-config
  • locale-encoding-mapping-list

Access Portlet Session as Anonymous User In WebSphere Portal

By default, WebSphere Portal does not enable session tracking for anonymous users, hence the following request.getPortletSession() code snippet will always return null for anonymous users:

private static SideNavigationPortletSessionBean getSessionBean(PortletRequest request) {
 PortletSession session = request.getPortletSession();
 if (session == null)
 return null; // <-- will always return null for anonymous users
 SideNavigationPortletSessionBean sessionBean = (SideNavigationPortletSessionBean) session.getAttribute(SESSION_BEAN);
 if (sessionBean == null) {
  sessionBean = new SideNavigationPortletSessionBean();
  session.setAttribute(SESSION_BEAN, sessionBean);
 }
 return sessionBean;
}

And this set the team wondering on how out of the box IBM portlets like Sitemap portlet (PA_SearchSitemapPort.ear) transports information to the presentation layers (JSP)? Hence we de-compiled the sitemap.jar and found out that they are transporting the information through the render parameters which is something that matches the paragraph found in Accessing the portlet session on the anonymous page:

If you need to enable session tracking across requests for non-authenticated users, you can do so by setting the public.session parameter in the portal Navigator service configuration or by setting the com.ibm.portal.public.session container run time option in a JSR 286 portlet deployment descriptor. Note that this may result in significantly increased memory consumption. … Instead of using these options, portlets that need to maintain interaction state even for non-authenticated users should use render parameters to keep this information instead of the portlet session, as recommended by the Java Portlet Specification.

 

Below is an example on how to pass information using render parameters?

  1. Under doView method, set the information to RenderRequest’s parameters
    public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {
     // ...
    
     request.setAttribute("rootPage", rootPage);
    
     // Invoke the JSP to render
     PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(JSP_FOLDER + VIEW_JSP);
     rd.include(request, response);
    }
  2. At JSP, retrieve the information as follows:
    <jsp:useBean id="rootElement" class="sg.xxx.xxx.sidenavigation.model.PageElement" scope="request"/>
    <%
     PageElement rootPage = (PageElement)request.getAttribute("rootPage");
     if (rootPage != null) {
      // logic ...
     }
    %>
    

WebSphere Portal: AuthorizationException Is Thrown While Calling Workspace getById Method

Below looks like an innocent block of codes written to retrieve WCM items:

 Content content = null;
 Workspace ws=WCM_API.getRepository().getWorkspace();
 DocumentIdIterator iter = ws.findByPath(path, Workspace.WORKFLOWSTATUS_ALL);

 if(iter.hasNext()) {
  DocumentId docId = iter.nextId();
  if (docId.isOfType(DocumentTypes.Content)) {
   content = (Content)ws.getById(docId,true,true);
  }
 }

But as soon as you try to retrieve WCM items with “User” access, the following “AuthorizationException” will be thrown (even though you are able to view the items in WCM Authoring Portlet):

  • com.aptrix.pluto.security.AuthorizationException
  • com.ibm.workplace.wcm.api.exceptions.AuthorizationException

This is because getById method by default required min “Contributor” rights. In order to resolve the issue, call Workspace.useUserAccess(true) before you call the getById method.

WebSphere Portal: Customize Authoring Template’s Elements With JSP

WebSphere Portal allows you to customize some of the Authoring Template’s elements look-and-feel using “Custom JSP” field and with some creativity, we can fulfil or further enhance our client’s WCM experience.

Naming a few requirements that we have encountered so far:

  • “Are you able to display the email’s WCM url in the Authoring Template (refers to the email that is send by WCM Workflow Email Action)?”
  • “Can we have a Color picker function like the JSColor (http://jscolor.com) for our sliding banners?”
  • “Can we have a dynamic product list where we can sort using drag and drop action?”
  • and so on…

Before we start, we need to decide where to store the custom jsp. There are currently 2 ways to call (reference) the custom jsp:

  • Method 1: We can store the jsp within PA_WCM_Authoring_UI and PA_WCMLRingPortJSR286 application folders. Then we reference the custom jsp by /jsp/html/xxx.jsp. PA_WCM_Authoring_UI and PA_WCMLRingPortJSR286 can be found at <wp_profile>\installedApps\<cell> (example: C:\IBM\WebSphere\wp_profile\installedApps\WPS80Cell).
  • Method 2 (Our Preferred Method): Create a web application to store the jsp and install the application via IBM Application Server console as “WebSphere enterprise applications”. Reference the jsp thru the context path as shown: contextPath;<jsp path> (example: “/MyCustomJSPs;/jsp/xxx.jsp“).This method is easier to maintain and less prone to cumulative fix patches.

Steps to create your own web application container using RAD 9.0:

  1. Create a new Web Project (File > New > Project > Web Project).
  2. Select “Basic” Project Templates and “Java EE” as programming model. Click on the “Next” button.
  3. Select “Web Module” and click on the “Finish” button.

What do you need to know?

In order to fully replicate existing Authoring Template’s behaviour (Read, Edit and Render modes), usually we would create at least 2 custom jsp (for Read and Edit modes). In some rare cases, we usually do not need Render mode to further “massage” the values in Presentation Template.

Below is a draggable Multiple Links example that we have created for one of our clients (I have omit out some of the codes to prevent the post from been draggy):

Step 1: Create AT-MultipleLinks_EditMode.jsp for Edit Mode

<%@ page import="com.ibm.workplace.wcm.api.authoring.CustomItemBean"%>
<%
 String ENTRY_DELIM = "\\<\\<\\<entry\\>\\>\\>";
 String VALUE_DELIM = "\\<\\<\\<delim\\>\\>\\>";

 CustomItemBean customItem = (CustomItemBean) request.getAttribute("CustomItemBean");
 String fieldName = customItem.getFieldName();
%>
<div class='<%=fieldName%>_related_links'><%
 String value = (String) customItem.getFieldValue();
 if(!value.equals("")){
  // render existing values
  String[] entries = value.split(ENTRY_DELIM);
  for (int i=0,len=entries.length;i<len;i++){
   String[] info = entries[i].split(VALUE_DELIM);
   if(info.length==2){%>
    <div class="entry">
     <div class="row">
      <div class="lbl">Description:</div>
      <div class="txt"><input type="text" class="lotusText" value="<%= info[0]%>" size="80" /></div>
     </div>
     <div class="row">
      <div class="lbl">Url:</div>
      <div class="txt"><input type="text" class="lotusText" value="<%= info[1]%>" size="80" /></div>
     </div>
     <div class="row">
      <div class="lbl"></div>
     </div>
    </div>
 <% }
  }
 }

 // set the javascript method to call when users clicks on the save button
 customItem.setSubmitFunctionName(fieldName+"_submit");
%>
</div>

// ... codes been omit out

<script>
<!-- submit method to collect the links' values and insert them to the hidden field
function <%=fieldName%>_submit(){
 var values = new Array();
 $(".<%=fieldName%>_related_links").find(".entry").each(function(){
  var info = $(this).find(".lotusText");
  if(info.length==2){
   var desc = info.get(0).value;
   var url = info.get(1).value;
   values.push(desc+"<%=VALUE_DELIM%>"+url);
  }
 });
 $("#<%=fieldName%>").val(values.join("<%=ENTRY_DELIM%>"));
}
</script>

Step 2: Create AT-MultipleLinks_ReadMode.jsp for Read Mode.
The read mode custom jsp is almost the same as edit mode except that it should only contains codes that are sufficient for reading purpose.

<%@ page import="com.ibm.workplace.wcm.api.authoring.CustomItemBean"%>
<%
 String ENTRY_DELIM = "\\<\\<\\<entry\\>\\>\\>";
 String VALUE_DELIM = "\\<\\<\\<delim\\>\\>\\>";

 CustomItemBean customItem = (CustomItemBean) request.getAttribute("CustomItemBean");
 String fieldName = customItem.getFieldName();
%>
<div class='<%=fieldName%>_related_links'><%
 String value = (String) customItem.getFieldValue();
 if(!value.equals("")){
  // render existing values
  String[] entries = value.split(ENTRY_DELIM);
  for (int i=0,len=entries.length;i<len;i++){
   String[] info = entries[i].split(VALUE_DELIM);
   if(info.length==2){%>
    <div class="entry">
     <div class="row">
      <div class="lbl">Description:</div>
      // noticed the readonly attribute?
      <div class="txt"><input type="text" class="lotusText" value="<%= info[0]%>" size="80" readonly /></div>
     </div>
     <div class="row">
      <div class="lbl">Url:</div>
      // noticed the readonly attribute?
      <div class="txt"><input type="text" class="lotusText" value="<%= info[1]%>" size="80" readonly /></div>
     </div>
     <div class="row">
      <div class="lbl"></div>
     </div>
    </div>
 <% }
  }
 }
%></div>

Step 3: Add in a Text element in Authoring Template and set the Custom JSP path

readMode=/wps/PA_WCM_Authoring_UI;/jsp/html/customJSP/AT-MultipleLinks_ReadMode.jsp,editMode=/wps/PA_WCM_Authoring_UI;/jsp/html/customJSP/AT-MultipleLinks_EditMode.jsp

Step 4: Create AT-MultipleLinks_RenderMode.jsp to render the result

After you have successfully create the render jsp, create a WCM JSP Component and point it to the render jsp. Call the JSP component that you have just created in the presentation template.

<%@taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ page import="com.ibm.workplace.wcm.api.*, java.util.ArrayList, javax.portlet.*"%>
<%
 String ENTRY_DELIM = "\\<\\<\\<entry\\>\\>\\>";
 String VALUE_DELIM = "\\<\\<\\<delim\\>\\>\\>";
 String I_WAN_TO_COMPONENT = "I Want To";

 Content content = null;
 RenderingContext renderingContext = (RenderingContext) request.getAttribute(Workspace.WCM_RENDERINGCONTEXT_KEY);
 if (renderingContext == null) {
  Object portletRequest = request.getAttribute("javax.portlet.request");
  if (portletRequest != null && portletRequest instanceof PortletRequest) {
   // JSR286 rendering
   renderingContext = (RenderingContext) ((PortletRequest) portletRequest).getAttribute(Workspace.WCM_RENDERINGCONTEXT_KEY);
  }
 }
 if (renderingContext != null) {
  content = renderingContext.getContent();
 }

 if (content != null && content.hasComponent(I_WAN_TO_COMPONENT)) {
  ContentComponent cmpt=content.getComponentByReference(I_WAN_TO_COMPONENT);
  if (cmpt instanceof TextComponent) {
   String iWantToLinks = ((TextComponent) cmpt).getText();

   if(!iWantToLinks.equals("")){
    out.print("<div class='i_want_to'><div class='title'>I Want To....</div><ul>");
    String[] entries = iWantToLinks.split(ENTRY_DELIM);
    for (int i=0,len=entries.length;i<len;i++){
     String[] info = entries[i].split(VALUE_DELIM);
     if(info.length==2){%>
      <li><a href='<%=info[1]%>'><%=info[0]%></a></li>
     <%}
    }
    out.print("</ul></div>");
   }
  }
 }
%>

 

More Examples:

WebSphere Portal: Setting up SMTP for WCM Workflow

Do the following to configure your SMTP server for WCM workflow notification:

  1. Log in to WebSphere Integrated Solutions Console (https://<server ip>:<port:10041>/ibm/console).
  2. Go to “Resource Environment > Resource Environment Providers” and click on “WCM WCMConfigService.
  3. Click on “Custom properties” link.
  4. Add in the following properties:
    1. connect.connector.mailconnector.defaultsmtpserver = Smtp server ip / hostname (mail.yourmailserver.com)
    2. connect.connector.mailconnector.defaultfromaddress = Default From email address (auto@yourmailserver.com)
    3. connect.connector.mailconnector.defaultreplytoaddress = Default Reply-to Field (auto@yourmailserver.com) *optional*
  5. If you are using a secure SMTP server, add in the following properties as well:
    1. connect.connector.mailconnector.defaultusername = Username
    2. connect.connector.mailconnector.defaultpassword = Password
  6. If your SMTP server is running other than port 25, add in “connect.connector.mailconnector.defaultsmtpport” and state the port.
  7. Restart portal.

 

How to customize the URL in WCM workflow notification email?

Usually we will help our clients to remove WCM port from the notification email. In order to do this, we would add in “wcm.authoringui.url=http://${WCM_HOST}/${WCM_WPS_CONTEXT_ROOT}/${WCM_WPS_PERSONALIZED_HOME}/wcmAuthoring” property in WCM WCMConfigService resource environment provider.

The default value for “wcm.authoringui.url” is:
http://${WCM_HOST}:${WCM_PORT}/${WCM_WPS_CONTEXT_ROOT}/${WCM_WPS_PERSONALIZED_HOME}/wcmAuthoring

WebSphere Portal: How to get HttpServletRequest and HttpServletResponse from Portlet Request?

Like most portlet developments, there are some scenarios where you might need to retrieve HttpServletRequest and HttpServletResponse from Portlet Request (RenderRequest/RenderResponse/ActionRequest/ActionResponse etc).

In WebSphere Portal, there is a PortletUtils utility class that provides the method you need:

import com.ibm.ws.portletcontainer.portlet.PortletUtils;

// call the respective methods to get HttpServletRequest/HttpServletResponse you need
PortletUtils.getHttpServletRequest(request);

[10/9/2014]: As highlighted by Stefan Schmitt, the above mentioned method is not supported by IBM. We found out about this class when we de-compiled “Sitemap” portlet. There is 7 or more PortletUtils types if you viewed it from your RAD “Quick fix”, use it at your own discretion.

Create New Theme in WebSphere Portal

This article shows you how to create a new theme in WebSphere Portal from v8.5 CF8  onward. There are other considerations that you might want to consider when you are creating a new theme for your clients. For example:

  • Should you enable friendly url in the portal (How to enable friendly url in WebSphere Portal Theme)?
  • Do you need to support localization? If not, do remember to remove locales support with this exercise to avoid nasty surprises.
  • How do you make IBM theme truly responsive? By default, IBM theme 8.5 will render desktop/tablet/mobile views according to browser’s User Agent. Which means despite iPad is able to support 1024 pixels, IBM theme will still renders your theme in tablet view.

 

This article was updated on 30 Oct 2015 to incorporate WebSphere Portal v8.5 CF8 updates such as DXSync and Theme Manager into the process.

Step 1: Install IBM Digital Experience File Sync for Static Resources Development

  1. DxSync only works fully with Node.js v0.12.* (as of WebSphere Portal v8.5 CF8). Pathwatcher module will not work if u are using the latest Node.js.
  2. Download the latest stable release of IBM Digital Experience File Sync from Github.
  3. Extract the file and run the “install.cmd” using cmd.
    20151028066

Alternatively, you can continue to use WebDAV client for static resources development. Our past favourite client is Cyberduck:

  • Connect to http://<server ip>:<port>/wps/mycontenthandler/dav/themelist via WebDAV client.
    Note: in our screenshot, you might notice that we are pointing to “/web/” instead of the default “/wps/“, please ignore that as we are trying out other configuration at the same time. By default, it will be pointing to “/wps/”.
    20131227003

 

Step 2: Setup DXSync and Copy The Static Resources for Your Theme

  1. Create new theme using Theme Manager. Click on “Theme Development” in the Applications menu.
    20160405140
  2. Click on the “Create Theme” button.
    20160405138
  3. Key in your theme’s title, description and select “Portal 8.5” as template.

    Note: In this step, I would prefer to remove the spaces for the theme’s title so that the theme folder created is more in-line with IBM themes naming convention (without spaces – see below screenshots).2016040513720151030072

  4. Click on the “Edit” (Gear) icon and edit the theme’s properties accordingly. Below are some of the common configuration that I would usually do:
    • General Tab
      • Add back the spaces for the theme’s Title *.
        20160405143
    • Skins Tab
      • Theme Manager by default will clone and install Portal 8.5 skins (Hidden, Hidden Plus, No Skin and Standard) and installed them as 4 separate new skins. This will cause skin duplication in your client’s environment (see the screenshot below).
        20160405147
        First identify the skin(s) that you are going to use and edit the skin’s Title, Localization, Unique Name and Static Content Root accordingly. For the unused skins, just make a “mark” in the skin’s title  as we are going to delete them in the later stage.
        20160405144
    • Localization Tab
      • Add back the spaces for the theme’s Title.
        20160405146
    • Advanced Tab
      • Edit the theme’s unique name accordingly and click on the “Done” button.
        20160405145
  5. Create a new local directory in your desktop for DXSync syndication with the new theme.
    20151030073
  6. In your command prompt, go to the local directory that you have just created and type “dxsync init“. Key in the necessary information.
    20151030074
    20151030075
  7. Start DXSync syndication by running the following command: “dxsync run“.
    20151030076
What we have done so far?
We have successfully setup DXSync and replicate the theme's static resources (as in the "frontend"), but the new theme is still referencing the default theme's dynamic resources (css/js/navigation, page menu, footer etc). 

To understand the static resources portion, go to your local DXSync theme folder <local_dxsync_theme_dir>/nls folder and play around with theme_en.html file.

 

Step 3: Remove Unwanted Skins

We have been experimenting around to find the easiest and “cleanest” (no skin entry in xmlaccess and WebDAV) way to delete skin. Below are our findings:

  1. Go to WebSphere Portal Administration page (http://<base portal ip>:10039/wps/myportal/Administration).
  2. Go to Portal User Interface > Themes and Skins.
    20160406148
  3. Delete the unwanted skins that you have “marked” earlier.
    20160406149
  4. Go to your local DXSync theme’s skin folder (<local_dxsync_theme_dir>/skins) and delete the unwanted skin folders. You will need to stop your DXSync in order to delete the folders.
    20160406150
  5. Run DXSync to sync up your changes. Don’t worry about the temporary error warning message (see screenshot below).
    20160406151

 

Step 4: Copy The Dynamic Resources for Your Theme

  1. Open your Eclipse or IBM® Rational® Application Developer and make sure Java EE developer tools add-on is installed.
    (note that the screenshots will varies between different RAD versions, I am currently using RAD 9.1)
  2. Click on File > New > Web Project.
    20160406153
  3. Key in your custom theme’s name and do the following:
    • Select “Simple” as Project Templates
    • Select “Java EE” as Programming Model
    • Click on the “Next” button
      20160406154
  4. Under Deployment tab, do the following:
    • Unchecked “Add support for WebSphere bindings and extensions”
    • Select “2.4” as Web module version
    • Checked “Add project to an EAR
    • Click on the “Finish” button
      20160406156
  5. Go to <portal_server_root>\theme\wp.theme.themes\default85\installedApps\DefaultTheme85.ear\DefaultTheme85.war and copy the following folders to your web project’s WebContent folder:
    • skins folder
    • themes folder
  6. Go to <portal_server_root>\theme\wp.theme.themes\default85\installedApps\DefaultTheme85.ear\DefaultTheme85.war\WEB-INF and copy the following files/folders to your web project’s WEB-INF folder:
    • decorations.xml
    • tld folder
    • plugin.xml
  7. Your web project’s file structure should looks similar like this:
    20160406158
  8. Edit the plugin.xml as follows:
    1. Change the following configuration to match your custom theme (see example below):
      • Plugin id and name (*important step*)
      • Extension id
      • Module id
        20160406157
    2. Edit all <title> and <description> tags to match your custom theme’s title and description
    3. Change all “85theme_” to your custom theme references (“ghostTheme_“)
      20160407158
    4. Remember to update the plugin.xml whenever you have add in a new dynamic resource.
  9. Follow the steps in WebSphere Portal: Specify White and Black List for Web Application to specify white and blacklist for your theme.
  10. Right-click your custom EAR project and export it as EAR file.
    20160407159
  11. Log on to the WebSphere® Integrated Solutions Console (https://<server ip>:10041/ibm/console) and go to Applications > Application Types > WebSphere enterprise applications.
  12. Click on the Install button and select your exported custom EAR file. Click on the Next button.
  13. Select Fast Path, expand Choose to generate default bindings and mappings, select Generate Default Bindings, and click Next button.
  14. Click on the Next button again (unless you wish to edit the default installation options).
  15. Checked the custom module and click on the Apply button. Click on the Next button.
  16. Click on the Finish button.
  17. When the EAR file is done installing, click Save directly to the master configuration link.
  18. Checked the custom application and click on the Start button.
  19. If the entire process is successfully, you should be able to find your new dynamic resources at <wp_profile_root>\installedApps\<cell>\<CustomThemeEAR.ear>\<CustomTheme.war> folder.
What we have done so far?
We have successfully replicate a new set of dynamic resources for our custom theme (as in the "backend codes"). But unlike Step 1, we are unable to test the changes immediately as our custom theme is still referencing the default theme's dynamic resources. Hence, this brings us to the 3rd step!

 

Step 5: Modify The Dynamic Resource References For Your Theme

  1. Ensure that your DXSync syndication for your custom theme’s static resources is still running.
  2. Go to your local static resources “<custom theme>/profiles” folder and replace all “wp_dynamicContentSpots_85occurrences in profile_*.json files to the new custom theme reference that you have defined in the plugin.xml (“geek_dynamicContentSpots_ghostTheme“).
  3. At the <custom theme>/nls folder, edit theme_en.html and replace all “85theme” occurrences to the new custom theme reference you have defined in the plugin.xml (“ghostTheme“). For example dyn-cs:id:85theme_head becomes dyn-cs:id:ghostTheme_head.
  4. Restart WebSphere Portal (even though I suspect that this step is not necessary).
What we have done so far?
We have successfully modify our custom theme to reference the new set of dynamic resources which we have created in Step 2. To verify, make changes to your dynamic resources jsp and restart WebSphere Portal, the changes should reflect.

 

Step 6: Enable JSP Auto-Reload In WebSphere Application Server

Do the steps listed in Enable Auto JSP Reload in WebSphere Portal to prevent the need to restart WebSphere Portal whenever you have make changes to the dynamic resources.

References:

  1. Theme Customization 8.0 Quick Reference
  2. Developing Themes for WebSphere Portal 8.5

WebSphere Portal: Theme Development Useful References

WebSphere Portal API

WebSphere Portal Library Tags | link
Our trusty library tags since WebSphere Portal 6.0 (maybe earlier)!
(The tags mentioned are only for use in theme and skin JSPs. Do not use portal tags in portlet JSPs.)

  • <portal-core/> tags – Used to provide portal core functionality such as entering the main render flow as well as URL-related aspects of the page.
  • <portal-dynamicui/> tags – Used to enable dynamic user interface features such as closing dynamic portlets and pages.
  • <portal-fmt/> tags – Used to provide enhanced portal formatting capabilities.
  • <portal-logic/> tags – Used to provide conditional logic.
  • <portal-navigation/> tags – Used to implement navigation tasks such as generating URLs and traversing the portal navigation model.
  • <portal-showtools/> tags – Used to provide administrative tools on the theme and skin level.
  • <portal-skin/> tags – Used to build a portlet title bar as well as make various functional icons available in the title bar.
  • <portal-theme-ext/> tags – Used to provide extended functionality to enhance the portal themes.

WebSphere Portal EL Beans | link
Since WebSphere Portal 7.0, Expression Language (EL) beans has been added for easy access to WebSphere Programming models. The beans are accessed in the global wp namespace. For more information on specific models, refer to the Portal 8.5 SPI Javadoc external link. Examples of the common used beans:

  • ${wp.metadata[node]}
  • ${wp.node.contentNode}
  • ${wp.identification[node]}
  • ${wp.node.metadata[‘metaDataKey’]}
  • ${wp.navigationModel}

Missing from the documentation:

  • “INTERNALURL” is missing from ContentNodeBean.contentNodeType documentation.

WebSphere Portal EL Available Variables (WebSphere Portal 8.0)
Do remember to check out bootstrap.jspf before init your variables, as some of the useful variables have already been initialized by the jsp fragment. Examples:

  • ${pageTitle} – current page’s title
  • ${currentNavNode} – current page’s navigation node
  •  ${aggMD} – an aggregated metadata of the current page
  • ${deviceClass} – device class, values available [<empty string>/tablet/smartphone]

Setting Dynamic Content Spot Mapping with mvc:URI | link
The mvc:URI scheme is a special URI format that accesses different resources, depending on the device class. This scheme is used by the Portal 8001 theme in the definition of several dynamic content spots. Example of possible combinations:

  • mvc:res:/hello.jsp: Uses a single default URI.
  • mvc:res:/hello.jsp,smartphone@res:/hello_smartphone.jsp: Uses res:/hello.jsp as the default URI and res:/hello_smartphone.jsp as the URI for smartphones.
  • mvc:res:/hello.jsp,smartphone/tablet@res:/hello_mobile.jsp: Uses res:/hello.jsp as the default URI and res:/hello_mobile.jsp as the URI for smartphones and tablets.
  • mvc:res:/hello.jsp,smartphone@,tablet@res:/hello_tablet.jsp: Uses res:/hello.jsp as the default URI and res:/hello_tablet.jsp as the URI for tablets. No URI is assigned for smartphones.

WebSphere Portal 8: Remove State Information From Friendly URL

By default, WebSphere Portal 8 will include navigational state information. While state information comes in handy by retaining the page’s state, it is often redeemed by our clients as messy and gibberish especially for public facing websites. An example of url with state information:

http://10.10.10.18:10039/wps/portal/test/!ut/p/a1/hc5BDoIwEAXQs3iCfptSyhIpUIyKRBO1G8OCYBOkBtDzWwwbF-LsJnl__hBNzkS35cvU5WBsWzakiLvOdoeq791-NPfKPocRaX7N9zJdUkHXqUqAMM89hsIHJHXg4gB-TIhPPkpDxfwNIKSMkMlVwHfcA2JM-Rnwp_9E9HcFmKDjBSX9YAtkfAIzLz5uTRJk9eINqfbQ7A!!/dl5/d5/L2dBISEvZ0FBIS9nQSEh/

Before we proceed, try this! Access your page’s url without state information. You will realize that you will be redirected to an url with state information.

Step 1: Add/Edit “friendly.redirect.enabled” Property in WebSphere Integrated Solutions Console:

  1. Login to WebSphere Integrated Solutions Console (https://<server ip>:<port:10041>/ibm/console).
  2. Click on “Resource Environment Provider“, under “Resources > Resource Environment > Resource Environment Providers“.
  3. Search for “WPConfigService” and click on it.
  4. Click on the “Custom Properties“.
  5. Search for “friendly.redirect.enabled” property. By default the property does not exists, add in “friendly.redirect.enabled” and set it to false (type as string).
  6. Save the setting to master configuration.
  7. Restart the server.

Step 2: Update Theme Parameter via XML Access

  1. Go to <PortalServer folder>\doc\xml-samples folder (for example: C:\IBM\WebSphere\PortalServer\doc\xml-samples).
  2. Locate ExportThemesAndSkins.xml and copy the file to <PortalServer folder>\bin folder.
  3. Open your cmd prompt and go to <PortalServer folder>\bin and key in the following command (press enter):
    xmlaccess.bat -in ExportThemesAndSkins.xml -out result.xml -user <wpsadmin's name> -password <wpsadmin's password> -url http://<server ip>:<port:10039>/wps/config
  4. Edit result.xml in notepad and locate the theme that you wish to remove the state information and add in ‘<parameter name=”com.ibm.portal.theme.hasBaseURL” type=”string” update=”set”>true</parameter>’ under the <theme> node (see example below).
    <?xml version="1.0" encoding="UTF-8"?>
    
    <request xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="PortalConfig_8.0.0.xsd" type="update">
       <!-- This sample sets the hasBaseURL Tag in the Portal 8 Theme. -->
       <portal action="locate">
          <theme action="update" uniquename="ibm.portal.80Theme" >
             <parameter name="com.ibm.portal.theme.hasBaseURL" 
                        type="string" update="set">true</parameter>
          </theme>
       </portal>
    </request>
  5. Rename your result.xml to configureTheme.xml (naming doesn’t matter).
  6. At your cmd prompt and execute the following cmd.
    xmlaccess.bat -in configureTheme.xml -out result.xml -user <wpsadmin's name> -password <wpsadmin's password> -url http://<server ip>:<port:10039>/wps/config
  7. You should be able to see the following response in your cmd prompt:
  8. To verify access one of the page’s url that is using the default theme without the state information (for example: http://10.10.10.18:10039/wps/portal/test). You will realize that you are no longer been redirect to a url with state information.
    Note: if you click on any of the links in the theme, you will still be directed to an url with state information and this brings us to the next step.

Step 3: Update Theme Dynamic Content Spots

  1. Go to <PortalServer folder>\theme\wp.theme.themes\default80\installedApps\DefaultTheme80.ear\DefaultTheme80.war\themes\html\dynamicSpots.
  2. Open navigation.jsp with an editor (would prefer Notepad++).
  3. Search for the string “<a href=”?uri=nm:oid:${nodeID}” and replace it with the following string:
    <a href="<portal-navigation:urlGeneration contentNode="${nodeID}" keepNavigationalState="false"><%wpsURL.write(out);%></portal-navigation:urlGeneration>"
  4. Repeat Step 2 and 3 with sideNavigation.jsp (in the same folder as navigation.jsp).

Step 4: Ensure that all of your pages have friendly name

  1. Scan thru all of your pages and ensure that they have friendly URL name.
  2. Congrats! You are done with this excerise!

References: