Tag Archives: WebSphere Portal 8.5

[Resolved] WebSphere Portal Bug: Clicking Any Button in Inline Editing Will Causes Chrome Scrollbar To Disappear

We have raised a PMR with IBM and they have provided us with a temporary workaround for CF9 and below (as of 18 Mar 2016). The full workaround should be in WebSphere Portal 8.5 CF10.

9 May 2016: We have confirmed that the bug has been resolved in WebSphere Portal 8.5 CF 10.

How to replicate the issue:

  • Ensure that your WebSphere Portal version is 8.5 CF9 and below
  • Ensure that your Chrome browser is up to date
  • Open any content that is long enough for the Chrome vertical scrollbar to appear in Inline Editing.
  • Click on any button and the Chrome scrollbar will disappear.

 

Below is IBM temporary workaround (as of 18 Mar 2016).

For WebSphere Portal 8.5 CF9 and below, modify the following files:

  • AuthoringUIView.jsp (<wp_profile>/installedApps/cellname/PA_WCM_Authoring_UI .ear/ilwwcm-authoring.war/jsp/html/AuthoringUIView.jsp)
  • dialogCloserLaunchPage.jsp ( <wp_profile>/installedApps/cellname/PA_WCM_Authoring_UI .ear/ilwwcm-authoring.war/jsp/html/dialogCloserLaunchPage.jsp)

with the following codes:


/*BEFORE: find the following lines */
dojo.addOnUnload(function() { 
 if (frameElement) { 
  frameElement.scrolling="no"; 
 } 
});

/*AFTER: change the code as follows */
dojo.addOnUnload(function() { 
 if (frameElement) { 
  if (i$.isChrome) { 
   frameElement.scrolling="auto"; 
  } else { 
   frameElement.scrolling="no"; 
  } 
 } 
}); 

There is an additional step for WebSphere Portal 8.5 CF8 and below:

  • Go to <PortalServer>/theme/wp.theme .modules/webapp/installedApps/ThemeModules.ear/ThemeModules .war/modules/dialog/js/.
  • Backup dialog_layer.js and dialog_layer.js.uncompressed.js.
  • Copy dialog_layer.js.uncompressed.js content to dialog_layer.js and modify the file with the following codes:

/*BEFORE: find the following lines */
// Don't display scrollbars until dialog content has loaded, this is set back to auto in onLoadFrame 
// Note: in chrome setting overflow style to hidden will not hide scrollbars 
f.setAttribute("scrolling","no");

/*AFTER: change the code as follows */
// Don't display scrollbars until dialog content has loaded, this is set back to auto in onLoadFrame 
// Note: in chrome setting overflow style to hidden will not hide scrollbars
if(i$.isChrome) { 
 f.setAttribute("scrolling","auto"); 
} else { 
 f.setAttribute("scrolling","no"); 
}

WebSphere Portal: Specify White and Black List for Web Application

If your SystemOut.log is throwing the following warning message, it means that you will need to specific white and black list for your custom web application:

[3/24/16 12:18:17:143 SGT] 0000027f AbstractReque W com.ibm.wps.resolver.resource.AbstractRequestDispatcherFactory matchesWebAppDefault(aResource) Servlet context [/WebApp] does not specify a blackwhite list when accessing resource [themes/html/dynamicSpots/custom/header.jsp], falling back to the default [[whitelist(null), blacklist(WEB-INF/.*)]]. Applications can define a custom list by adding the keys [com.ibm.portal.resource.whitelist] and [com.ibm.portal.resource.blacklist] to their web.xml deployment descriptor. For details see information for APAR PI47714 related to CVE-2014-8912 (Security bulletin: http://www.ibm.com/support/docview.wss?uid=swg21963226).

Add the following parameters to your web.xml and redeployed your application:

<web-app> 
...
<context-param>
 <description>A regular expression that defines which of the resources in the war file can be served by the portal res datasource.</description> 
 <param-name>com.ibm.portal.resource.whitelist</param-name> 
 <param-value>.*</param-value>
</context-param> 
<context-param>
 <description>A regular expression that defines which of the resources in the war file cannot be served by the portal res datasource.</description> 
 <param-name>com.ibm.portal.resource.blacklist</param-name> 
 <param-value>WEB-INF/.*</param-value>
</context-param>
... 
</web-app> 

How to Remove Sign Up Link in WebSphere Portal Theme?

20160229125

WebSphere Portal theme will show Sign Up link by default for anonymous users. We can remove the link by deleting “Anonymous Portal User” from USER SELF ENROLLMENT virtual resources Editor role.

Do the following steps to remove Sign Up Link in WebSphere Portal Theme:

  • Login to WebSphere Portal using your portal administrator account.
  • Go to Portal Administration page (http://<hostname>/Administration).
  • Go to “Access > Resource Permissions” and click on “Virtual Resources” (It should be on the 2nd page).
    20160229126
  •  Click on USER SELF ENROLLMENTAssign Access” button.
    20160229127
  • Click on EditorEdit Role” button.20160229128
  • Remove “Anonymous Portal User” from Editor Role.
    20160229130
  • Now logout to verify the changes. The Sign Up link should be remove for Anonymous Portal User now.20160229131

WebSphere Portal: Remove “/wps/portal” from URL

IBM has finally provides us a way to remove "/wps/portal" in WebSphere Portal *pops champagne*. Do take note that this only applies to WebSphere Portal v8.5 CF8 onwards.

Take note (as of WebSphere Portal v8.5 CF8): After you have successfully remove “/wps/portal”, the system will not be unable to locate all previous custom themes that you have created earlier. You will need to run xmlaccess manually to remove “/wps” from the theme context-path.
“EJPFD0097E: No theme was found to render the page. Navigate to Administration and assign a working theme to restore full function to your site.”

 

Do the following steps to remove “/wps/portal”:

  1. Start cw_profile. Open your cmd and go to <app_server>\ profiles\cw_profile\bin and type “startServer.bat server1“.
  2. Go to Configuration Wizard (http://<server_ip>:10200/ibm/wizard).
  3. Login in with admin ID for cw_profile profile.
  4. Click “Set Up a Stand-alone Server > Modify Site URLs for Search Engine Optimization” or “Set Up a Cluster > Modify Site URLs for Search Engine Optimization” depending on your environment type.
  5. Click on “URL Settings” and select “Yes” for “Do you want to modify or remove your context root” field blank. (as of WebSphere Portal v8.5 CF8) I would not remove navigation state information using this method as this will cause default portal 8.5 theme to lose its navigation state as well. A better method will be this.
  6. Click on the “Next” arrow.
  7. Key in the required admin information and leave “Context root” and “
  8. Click on “Start Configuration” button.
  9. Click on “OK” button.
  10. The wizard will pause at certain manual steps. Click on the “Instructions” link to follow through the manual configuration.
  11. Congrats! You have remove “/wps/portal” from the url!

 

WebSphere: How to Access Portal Theme Tags in JSP?

Add “<%@ taglib uri=”/WEB-INF/tld/portal.tld” prefix=”portal” %>” in order to access the portal theme tags like “<portal-logic/>” and “<portal-navigation/>“.

To call the tag function, simply call <portal:<function name>/>. For example in portal theme, we would call:


<portal-logic:if loggedIn="yes">...</portal-logic:if>

to


<portal:if loggedIn="yes">...</portal:if>

WebSphere Portal is Throwing “Could not load ‘dojo.nls.dojo_en'” error

We have logged a PMR with IBM recently about the browser is complaining that it could not load ‘dojo.nls.dojo_en’.

could not load dojo.nls.dojo_en

And IBM official response is to apply Deferred with Dojo (“profiles/profile_deferred.json”) theme profile directly to the page and this resolves the issue for me.

How to apply “Deferred with Dojo” profile:

  1. At the front-end, turn on the “Edit Mode“.
  2. Click on Menu (below Edit Mode) > Edit Page Properties.
  3. Click on the Advanced tab and select “Deferred with Dojo” Profile.
  4. Click on the “Save” button.

 

Note: A simple method (which I like) is to add in “resourceaggregation.profile” page property and set “profiles/profile_dojo_deferred.json” as its value.

 

How to Create WebSphere Portal Custom Workflow Action?

IBM allows us to customize our own workflow action. It will comes in handy when you need the system to:

  • Insert/remove a reference database record whenever a content get published/expired
  • Send email notification to news subscribers whenever a news content get published
  • Customize the default WCM email notification message

Disclaimer: This article follows the steps listed in Creating a custom workflow action class and we have replaced WebContentCustomWorkflowService with WcmCustomWorkflowService. At this point, we can’t confirm if WcmCustomWorkflowService is the correct class replacement.

Steps to create your custom workflow action:

  1. Open your Eclipse or IBM® Rational® Application Developer and make sure Java EE developer tools add-on is installed.
  2. Click on File > New > Web Project.
  3. Key in your custom workflow project’s name. Select “Simple” as Project Templates and “Java EE” as Programming Model.
  4. Under Deployment tab, select “2.4” as Web module version and checked “Add project to an EAR“.
    Click on the “Finish” button.
  5. Create a java class that implements the interface com.ibm.workplace.wcm.api.custom.CustomWorkflowAction. This class is responsible for executing your codes.
    Note: In this example, we will only be doing System.out.println to prove that the custom workflow action works

    package xxx.xxx.xxx.cwf;
    
    import java.util.Date;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import com.ibm.workplace.wcm.api.Document;
    import com.ibm.workplace.wcm.api.WcmCustomWorkflowService;
    import com.ibm.workplace.wcm.api.custom.CustomWorkflowAction;
    import com.ibm.workplace.wcm.api.custom.CustomWorkflowActionResult;
    import com.ibm.workplace.wcm.api.custom.Directive;
    import com.ibm.workplace.wcm.api.custom.Directives;
    
    public class NewsAlertSubscriptionAction implements CustomWorkflowAction {
    
     @Override
     public CustomWorkflowActionResult execute(Document doc) {
      // Put your customized trigger codes here
      System.out.println("Executing NewsAlertSubscriptionAction");
      String msg = "";
      InitialContext initCtx = null;
      WcmCustomWorkflowService customWorkflowService = null;
      CustomWorkflowActionResult result = null;
    
      try {
       initCtx = new InitialContext();
       customWorkflowService = (WcmCustomWorkflowService) initCtx.lookup(WcmCustomWorkflowService.JNDI_NAME);
      } catch (Exception e) {
        msg = " - System has encountered exception (do check logs).";
        e.printStackTrace();
      }
    
      // directive: indicate if the content should proceed to the next stage
      // Check out WCM Javadoc for more valid directives information
      Directive directive = Directives.CONTINUE;
      System.out.println(" - document:" + doc.getName());
      return customWorkflowService.createResult(directive, msg);
     }
    
     @Override
     public Date getExecuteDate(Document arg0) {
      return DATE_EXECUTE_NOW;
     }
    }
    
  6. Create a custom workflow action factory class that implements the interface com.ibm.workplace.wcm.api.custom.CustomWorkflowActionFactory. This is the controller class, it is use to call the respective custom workflow actions.
    package xxx.xxx.xxx.cwf;
    
    import java.util.Locale;
    import com.ibm.workplace.wcm.api.Document;
    import com.ibm.workplace.wcm.api.custom.CustomWorkflowAction;
    import com.ibm.workplace.wcm.api.custom.CustomWorkflowActionFactory;
    
    public class XXXCustomWorkflowActionFactory implements CustomWorkflowActionFactory {
     String NEWS_ALERT_SUBSCRIPTION = "News Alert Subscription";
    
     @Override
     public CustomWorkflowAction getAction(String arg0, Document arg1) {
      if (arg0.equals(NEWS_ALERT_SUBSCRIPTION)) {
       return new NewsAlertSubscriptionAction();
      }
      return null;
     }
    
     @Override
     public String getActionDescription(Locale arg0, String arg1) {
      if (arg0.equals(NEWS_ALERT_SUBSCRIPTION)) {
       return "Send Email Alert to Subscribers";
      }
      return null;
     }
    
     @Override
     public String[] getActionNames() {
      String names[] = { NEWS_ALERT_SUBSCRIPTION };
      return names;
     }
    
     @Override
     public String getActionTitle(Locale arg0, String arg1) {
      return arg1;
     }
    
     @Override
     public String getName() {
      return "XXX Custom Workflow Actions";
     }
    
     @Override
     public String getTitle(Locale arg0) {
      return "XXX Custom Workflow Actions";
     }
    }
    
    
  7. Create a plugin.xml file if you are deploying using WAR or EAR. Include the plugin.xml file in the application’s “WEB-INF” folder.
    <?xml version="1.0" encoding="UTF-8"?>
    <plugin id="XXXCustomWorkflowActionsPlugin" name="XXX Custom Workflow Actions Plugin" version="1.0.0" provider-name="IBM">
    <extension point="com.ibm.workplace.wcm.api.CustomWorkflowActionFactory" id="XXXCustomWorkflowActionFactory">
    <provider class="xxx.xxx.xxx.cwf.XXXCustomWorkflowActionFactory"/>
    </extension>
    </plugin>
    
  8. Follow the Deploying custom plug-in applications to deploy your plugin.
  9. Go to Web Content Authoring portlet and create Custom Workflow Action, you should be able to select your custom workflow action as shown below.

 

WebSphere Portal 8.5: Customize Ephox Editor

Updates (6-Jul-2015): 
With Chrome (version 42) finally dropping support for NPAPI, we can only hope that Ephox Textbox.io can catch up with its predecessor - http://docs.ephox.com/display/IBMWCMTB/Textbox.io+for+IBM+WCM+Home.

We have been encouraging our clients to use Ephox Editor as their default WCM rich text editor ever since IBM has acquired Ephox Editor OEM licence. Hence it is important for us to know how to:

  • Customize Ephox Editor default menu and toolbar items (for example change the default font size from “pt” to “px”)
  • Apply our custom theme css classes styles in Ephox Editor

Most of the customization will be taking place at the config file, it is located at  <wp_profile>/installedApps/<cell>/wcm.ear/editor-editlive-config.war/config/config.xml.jsp.

 

Steps to customize Ephox Editor default editor settings:

  1. Read Setting Menu and Toolbar Items tutorial guide first.
  2. Edit the config file accordingly.
  3. Restart the server.

 

Steps to include custom theme css in Ephox Editor:

  1. Read Using CSS in the Applet tutorial guide first.
  2. Edit the config file accordingly.
  3. Restart server.

 

Query Service in WebSphere Portal WCM API

QueryService is a good addition to WCM API. It helps to keep the API neat and provides a way for developers to do “OR” condition (Disjunction class).

Below is a simple example on how to do a “Like” search on content’s title
(Do take note that Selectors.titleLike method is case-sensitive (as of 8.5) <- hope they will add in extra argument in future to allow us to decide if the title search should be case-sensitive)


Repository repository = WCM_API.getRepository();
Workspace ws = repository.getWorkspace();
ws.login();

QueryService queryService = ws.getQueryService();
Query query = queryService.createQuery(Content.class);
query.addSelector(Selectors.titleLike("%a%")); //searching for contents' title containing "a"

query.addSort(Sorts.byPublishDate(SortDirection.DESCENDING)); // sort by publish date
ResultIterator resultIter = queryService.execute(query);
while (resultIter.hasNext()) {
 Content content = (Content) resultIter.next();
 // process...
}

 

But we noticed that there are performance deficits when we try to traverse > 400 contents (~11 sec to complete). The deficit occurs when we tried to cast the object into its respective object types (for example (Content) resultIter.next()). We believe that the API is trying to clone the content’s internal document (including its respective elements).

Hence if you just need to reference the contents, perhaps you can try to retrieve content using the traditional Workspace.getById(DocumentId<T> id, boolean asReference, boolean loadElements) method (as this effectively reduce our time from 11 sec to just 1 sec on our side). Do let us know if it works for you, cheers!


Repository repository = WCM_API.getRepository();
Workspace ws = repository.getWorkspace();
ws.login();

QueryService queryService = ws.getQueryService();
Query query = queryService.createQuery(Content.class);
query.returnIds(); // get the query services to return DocumentId instead of Document objects
query.addSelector(Selectors.titleLike("%a%")); //searching for contents' title containing "a"

query.addSort(Sorts.byPublishDate(SortDirection.DESCENDING)); // sort by publish date
ResultIterator resultIter = queryService.execute(query);
while (resultIter.hasNext()) {
 Content content = (Content) ws.getById(((DocumentId) resultIter.next()),true,false); // only retrieve a reference Content without loading its elements
 // process...
}

 

Disjunction (Our favourite “OR” condition)

Below is a code snippet on how to retrieve contents based on “OR” categories condition (Disjunction).


Disjunction orCondForCats = new Disjunction();
// tranverse contents if they contains one of the selected categories
for (int i = 0, len = selectedList.size(); i &lt; len; i++) {
 orCondForCats.add(ProfileSelectors.categoriesContains(selectedList.get(i)));
}
query.addSelector(orCondForCats);

 

Conclusion:
We certainly will be using Query Service more often for our future projects. We also felt that the method has room  for improvements:

  • Provide a new execute method (similar to Workspace.getById(DocumentId<T> id, boolean asReference, boolean loadElements)) which allow developers to decide if the documents retrieve should be editable and loaded with elements. This should effectively improve the Query Service performance.
  • Provide an option to allow developers to perform case-insensitive search for content’s title.

WebSphere Portal: Using WCM_API for Selected Virtual Portal

I was writing a live-feed migration portlet (AJAX) for my client when I realized that calling Workspace in Servlet is actually pointing to the base portal (which somehow makes sense). Hence I checked WCM API for updates and that was where I found Repository.generateVPContextFromHostname and Repository.executeInVP methods. The methods look promising but I have no idea how to call it until I found Philip Cheshire’s blog. Credits goes to him!

Do note that objects taken out from executeInVP method will not be able to access their repository and all actions will be performed as if the item belonged to the base portal workspace. There is one instance where I wrote a VirtualPortalScopedAction object to return only the Workspace and that is how I realized that the Workspace is “referencing” back to the default base repository.

Below illustrate a simple example on how to retrieve Authoring Template’s DocumentId from a specific Virtual Portal

In Our Servlet:


@WebServlet("/ProcessCircular")
public class ProcessCircular extends HttpServlet {
 public ProcessCircular() {
  super();
  try {
   Repository repository = WCM_API.getRepository();
   VirtualPortalContext vpc = repository.generateVPContextFromHostname(MigrationScriptPortlet.VIRUTAL_PORTAL_HOSTNAME);

   // retrieve AT document id
   VPScopedActionAT vpsAT = new VPScopedActionAT();
   repository.executeInVP(vpc, vpsAT);
   DocumentId atId = vpsAT.docId; // this is the part where we take object out from executeInVP
  } catch (WCMException e) {
   // ...
  }
 }
}

 

Then we proceed to create VPScopedActionAT that implements VirtualPortalScopedAction:

public class VPScopedActionAT implements VirtualPortalScopedAction {
 public DocumentId docId;

 @Override
 public void run() throws WCMException {
  Repository repository = WCM_API.getRepository();
  Workspace ws = repository.getSystemWorkspace();
  ws.setCurrentDocumentLibrary(ws.getDocumentLibrary(MigrationScriptPortlet.CONTENT_LIB));
  DocumentLibrary docLib = ws.getDocumentLibrary("Corporate");

  DocumentIdIterator atIter = ws.findByName(DocumentTypes.AuthoringTemplate, MigrationScriptPortlet.CIRCULAR_AT);
  if (atIter.hasNext()) {
   docId = atIter.next();
  }
  repository.endWorkspace();
 }
}