Portlet menus


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, your 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 wp_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, it is recommended to store the menu tree instance in a portlet session attribute or using the CachedStateService. 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 the Portal Toolkit to create a portlet that adds a simple static menu to the portal navigation. To get started, open WebSphere Studio in the portlet perspective.

  1. Use the Create a Portlet Project wizard to create a basic portlet. On the Portlet selection panel, choose Basic portlet.

  2. Add the following configuration parameter to the concrete portlet definition in portlet.xml. 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>
    

  3. To verify the correct XML syntax while editing menu-tree.xml, extract the DTD file menu-tree.dtd from the package com.ibm.wps.portlet.menu into the same folder.

  4. Create the menu tree XML in the /WEB-INF folder. You can create the XML from the DTD using File then click New then click Other then click XML then click XML and then selecting Create XML file from a DTD file. Be sure to specify the public ID: -//IBM//DTD Portlet Menu 1.0//EN . Populate the menu tree with a number of menu nodes. The following is the example provided in the Javadoc for XMIMenuTreePortlet.
         <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>
    

  5. 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.*;
    

  6. Change the class declaration to extend XMIMenuTreePortlet:
    public class MyPortlet extends XMIMenuTreePortlet implements MenuProvider, ActionListener

  7. 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);
             }
          }
       }
    
    
    Change the class declaration to extend XMIMenuTreePortlet
    public class MyPortlet extends XMIMenuTreePortlet {

  8. Save your changes.

You 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 XMIMenuTreePortlet:
    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. 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);
               }
            }
         }
    

  5. 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.

    The CachedStateService is recommended to store the menu tree instead of the session attribute. The scope of the menu tree defines how often the tree is rebuilt in the navigation framework. See the Javadoc for programming details.

      // 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;
      }
    

  6. Save your changes.

You 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.

See also