Portlet menus

 

+

Search Tips   |   Advanced Search

 

The information in this topic applies only to the IBM Portlet API.

Portlet menus allow a portlet to add menu entries to the navigation tree of the portal. Menu items are automatically positioned in the navigation tree after the page that contains the portlet instance. The root node of this tree represents the portlet and all child nodes represent portlet menu entries. Portlet menu entries do not belong to the internal portal model. For example, they cannot be modified or removed using the page customizer.

The portlet menu nodes contain a URL that either points to the portlet itself or to an external page. When the menu nodes refer to the portlet, the URL includes an action string on which the portlet code can react, and one or more request parameters, which the portlet can use. It is not possible that the menu nodes point to another portlet or page of the portal than the portlet which created them.

To add menu entries to the portal navigation tree, portlets must implement the MenuProvider interface. The portlet implementing MenuProvider must provide a getMenu() method, which returns a tree model instance of the MenuTree class to display in the portal navigation. To create a new instance of a MenuTree, the getMenuTree() method of one of the provided service classes can be used. They are located in the package com.ibm.wps.portletservice.portletmenu.

To simplify the development of portlet menus, the portlet should extend one of the provided helper classes that implement the MenuProvider interface. These helper classes create the instance of MenuTree using the PortletService classes. The portlet menu helper classes are located in the com.ibm.wps.portlets.menu package in wpsportlets.jar. The Javadoc for the helper classes can be found in the portal_server_root/dev/samples directory.

The choice of which helper class to use depends on how the portlet should implement the menu:

Dynamic menus

tree structure is defined in the portlet code. Menu items are added dynamically for each request; the portlet is queried for menu information for each request so that the portlet can return stateful information in the menu entry. For performance reasons, IBM recommends to store the menu tree instance in a portlet session attribute. This type of menu can be created by extending the MemoryMenuTreePortlet helper class, which internally uses the MemoryMenuService.

Static menus

tree structure is defined in an XMI-formatted deployment descriptor extension packaged with the portlet. The static method allows portlets to declare menu entries in the deployment descriptor that are fixed. When the menu entries have to be changed on subsequent requests, the dynamic method has to be used. This type of menu can be created by extending the XMIMenuTreePortlet class.

 

Create a portlet that implements a static menu

The following steps show how to use Rational Application Developer to create a portlet that adds a simple static menu to the portal navigation. To get started, open Rational Application Developer in the Web perspective.

  1. From the menu bar of Rational Application Developer, click...

    File | New | Portlet Project
    Do not select the JSR 168 portlet project. The New Portlet Project wizard is displayed.

  2. Enter a project name, make sure Create a portlet is selected and click Next.

  3. For portlet type, select Basic portlet and click Finish. The new portlet project is displayed in the Project Explorer.

  4. Add the following configuration parameter to the concrete portlet definition in the portlet deployment descriptor. This parameter specifies the file path of the menu tree descriptor relative to the /WEB-INF folder.

       <config-param>
          <param-name>XMIFilePath</param-name>
          <param-value>menu-tree.xml</param-value>
       </config-param>
    

  5. Create the menu tree XML file as follows.

    1. In the Project Explorer, locate the menu-tree.dtd in the project by expanding...

      Java Resources | WebSphere Portal v5.1 Runtime | com.ibm.wps.portlet.menu

    2. Open menu-tree.dtd and save it to the /WEB-INF folder of the project.

    3. From the menu bar, click...

      File | New | Other | XML | Create XML file from a DTD file

      The Create XML File wizard opens.

    4. Select Create XML file from a DTD file and click Next.

    5. To specify the file name, make sure the parent folder is set to....

      project_name/WebContent/WEB-INF
      Enter menu-tree.xml for the file name, and click Next.

    6. To specify the DTD file, click Select file from workbench:, select...

      /WebContent/WEB-INF/menu-tree.dtd

      ...and click Next.

    7. Enter...

      -//IBM //DTD Portlet Menu 1.0//EN

      for the Public ID and click Finish. The new XML file is opened in the editor area.

    8. Populate the menu tree with a number of menu nodes. The following is the example provided in the Javadoc for XMIMenuTreePortlet.

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE menu-tree PUBLIC "-//IBM //DTD Portlet Menu 1.0//EN" "menu-tree.dtd" >
        <menu-tree>
           <menu-node id="rootId">
               <display-info>
                   <locale>en</locale>
                   <title>XMI Test Portlet</title>
                   <description>XMI Tree Description</description>
               </display-info>
               <display-info>
                   <locale>fr</locale>
                   <title>XMI Test Portlet</title>
                   <description>XMI Tree Description</description>
               </display-info>
               <!-- node 1 -->
               <menu-node id="id1">
                   <display-info>
                       <locale>en</locale>
                       <title>Node 1</title>
                       <description>Node 1 Description</description>
                   </display-info>
                   <display-info>
                       <locale>fr</locale>
                       <title>Noeud 1</title>
                       <description>Noeud 1 Description</description>
                   </display-info>
                   <action-name>Node1Action</action-name>
                   <param id="n1-pid1">
                       <param-name>Param1Name</param-name>
                       <param-value>Node1Param1Value</param-value>
                   </param>
                   <param id="n1-pid2">
                       <param-name>Param2Name</param-name>
                       <param-value>Node1Param2Value</param-value>
                   </param>
                   <!-- node 1.1 -->
                   <menu-node id="id1_1">
                       <display-info>
                           <locale>en</locale>
                           <title>Node 1.1</title>
                           <description>Node 1.1 Description</description>
                       </display-info>
                   </menu-node>
                   <!-- node 1.2 -->
                   <menu-node id="id1_2">
                       <display-info>
                           <locale>en</locale>
                           <title>Node 1.2</title>
                           <description>Node 1.2 Description</description>
                       </display-info>
                   </menu-node>
               </menu-node>
               <!-- node 2 -->
               <menu-node id="id2">
                   <display-info>
                       <locale>en</locale>
                       <title>Node 2</title>
                       <description>Node 2 Description</description>
                   </display-info>
                   <display-info>
                       <locale>fr</locale>
                       <title>Noeud 2</title>
                       <description>Noeud 2 Description</description>
                   </display-info>
                   <action-name>Node2Action</action-name>
                   <param id="n2-pid1">
                       <param-name>Param1Name</param-name>
                       <param-value>Node2Param1Value</param-value>
                   </param>
                   <param id="n2-pid2">
                       <param-name>Param2Name</param-name>
                       <param-value>Node2Param2Value</param-value>
                   </param>
               </menu-node>
           </menu-node>
        </menu-tree>
      

  6. In the Java source, add the following import statements:

    import com.ibm.wps.portlet.menu.*;
    import com.ibm.wps.portlets.menu.*;
    import org.apache.jetspeed.portlet.event.*;
    

  7. Change the class declaration to extend XMIMenuTreePortlet:

    public class MyPortlet extends XMIMenuTreePortlet 
                 implements MenuProvider, ActionListener
    

  8. Implement the method actionPerformed() of the ActionListener interface. For example:

    public void actionPerformed(ActionEvent event) throws PortletException
    {
       if (event.getAction()!=null)
       {
    
          String actionString = event.getActionString();
          PortletRequest request = event.getRequest();
          if (actionString != null)
          {
             request.setAttribute("ActionString", actionString);
          }
       }
    }
    

  9. Change the class declaration to extend XMIMenuTreePortlet

    public class MyPortlet extends XMIMenuTreePortlet 
    {
    

  10. Save the changes.

We can debug the portlet locally or export the portlet to a WAR file and test it on a remote server.

 

Create a portlet that implements a dynamic menu

The following steps show how to create a dynamic portlet that adds a simple dynamic menu to the portal navigation.

  1. In the Java source, add the following import statements:

      
      import com.ibm.wps.portlet.menu.*;
      import com.ibm.wps.portlets.menu.*;
      import org.apache.jetspeed.portlet.event.*;
    

  2. Change the class declaration to extend MemoryMenuTreePortlet :

       public class MyPortlet extends MemoryMenuTreePortlet implements MenuProvider, ActionListener
    

  3. Implement the method actionPerformed() of the ActionListener interface. For example:

    public void actionPerformed(ActionEvent event) 
                throws PortletException  
    {
    
       if (event.getAction()!=null) 
       {
          String actionString = event.getActionString();
          PortletRequest request = event.getRequest();
          if (actionString != null)
          {
             request.setAttribute("ActionString", actionString);
          }
       }
    }
    

  4. Overwrite the method getMenuTree() of the superclass, which wraps the method getMenu() of the MenuProvider interface (see example below). The method createEmptyMenuTree() uses the MemoryMenuService to create the tree. When a user selects the portlet for the first time, the menu tree is generated from the portlet source code and stored in a session attribute. When the getMenu() method is called the next time, the tree is already available.

    // overwrites implementation of MemoryMenuTreePortlet public MenuTree getMenuTree(MenuContext menuContext) 
                    throws PortletException {
    
       // session stores menu tree    PortletSession session = 
                      menuContext.getPortletRequest().getPortletSession();
       MenuTree menuTree = (MenuTree) session.getAttribute("menu-tree");
    
       if ( menuTree == null )
       {
    
         // use method implementation of MemoryMenuTreePortlet      menuTree = super.createEmptyMenuTree(menuContext);
    
          // get the controllers       MenuTreeInfoCtrl  ctrlInfo    = (MenuTreeInfoCtrl) menuTree;
          MenuTreeTopologyCtrl ctrlTopology  = 
                               (MenuTreeTopologyCtrl) menuTree;
    
          try       {
            // get the root         MenuNode rootNode = menuTree.getRoot();
    
            // set scope         ctrlTopology.setScope(rootNode,MenuNode.SCOPE_REQUEST);
    
            // add root node title and description         ctrlInfo.setInformation(Locale.ENGLISH,"Memory Tree","Memory Tree",rootNode);
            ctrlInfo.setInformation(Locale.FRENCH,"Memory Tree","Memory Tree",rootNode);
    
            // add two new nodes with title and description         MenuNode node1 = ctrlTopology.addNode("id1",rootNode);
            ctrlInfo.setInformation(Locale.ENGLISH,"Node 1","Node 1",node1);
            ctrlInfo.setInformation(Locale.FRENCH,"Noeud 1","Noeud 1",node1);
    
            MenuNode node2 = ctrlTopology.addNode("id2",rootNode);
            ctrlInfo.setInformation(Locale.ENGLISH,"Node 2","Node 2",node2);
            ctrlInfo.setInformation(Locale.FRENCH,"Noeud 2","Noeud 2",node2);
    
            MenuNode node3 = ctrlTopology.addNode("id3",rootNode);
            ctrlInfo.setInformation(Locale.ENGLISH,"Node 3","Node 3",node3);
            ctrlInfo.setInformation(Locale.FRENCH,"Noeud 3","Noeud 3",node3);
    
            // add two subnodes to node1 with title and description         MenuNode node1_1 = ctrlTopology.addNode("id1_1",node1);
            ctrlInfo.setInformation(Locale.ENGLISH,"Node 1.1","Node 1.1",node1_1);
            ctrlInfo.setInformation(Locale.FRENCH,"Noeud 1.1","Noeud 1.1",node1_1);
    
            MenuNode node1_2 = ctrlTopology.addNode("id1_2",node1);
            ctrlInfo.setInformation(Locale.ENGLISH,"Node 1.2","Node 1.2",node1_2);
            ctrlInfo.setInformation(Locale.FRENCH,"Noeud 1.2","Noeud 1.2",node1_2);
    
            // set action and add parameter-value pairs to node1
            ctrlInfo.setAction("Node1Action",node1);
            ctrlInfo.addActionParameter("Param1Name","Node1Param1Value",node1);
            ctrlInfo.addActionParameter("Param2Name","Node1Param2Value",node1);
    
            // set action and add parameter-value pairs to node2
            ctrlInfo.setAction("Node2Action",node2);
            ctrlInfo.addActionParameter("Param1Name","Node2Param1Value",node2);
            ctrlInfo.addActionParameter("Param2Name","Node2Param2Value",node2);
    
            // set url to node3
            ctrlInfo.setURL("http://www.ibm.com/us/",node3);
    
          }
          catch ( MenuTreeException ex )
          {
            throw new PortletException("Exception creating tree", ex);
          }
    
          session.setAttribute("menu-tree", menuTree);
        }
    
        return menuTree;
      }
    

  5. Save the changes.

We can debug the portlet locally or export the portlet to a WAR file and test it on a remote server.

For more information and examples for using portlet menus, see the Javadoc in the com.ibm.wps.portlet.menu, com.ibm.wps.portlets.menu, and com.ibm.wps.portletservice.portletmenu packages. The Javadoc includes some very detailed examples of how to implement portlet menus.

 

Related information