4. Adding a New Menu or Menu Item

Classes that inherit from the ZenMenuable mixin have a method called getMenus, which traverses up the object's path aggregating ZenMenuItem objects owned by its ancestors. These objects comprise an action to be executed, a human-readable description, and various attributes restricting the objects to which the item is applicable.

For example, imagine basic menus exist on dmd and dmd.Devices:

    dmd
        More              (menu)
            See more...   (menu item)
            Do more...
        Manage
            Manage object...
    dmd.Devices
        More
            See more...
            Do less...

A call to dmd.Devices.getMenus() will return:

    More
        See more...      (from dmd.Devices)
        Do more...       (from dmd)
        Do less...       (from dmd.Devices)
    Manage
        Manage object... (from dmd)

As you can see, menu items inherit their ancestors' unless they define their own, which override when their ancestors' conflict.

In theory, all ZenMenuables (which includes nearly all objects in Zenoss) may own menu items; in practice, all but a few menus live on /zport/dmd.

Adding a new menu item is fairly straightforward. Because menu items are persistent objects, modifications must happen in a migrate script (or be included as XML in a ZenPack). The method ZenMenuable.buildMenus accepts a dictionary of menus, each of which is a list of dictionaries representing the attributes of menu items. Instructions on writing migrate scripts can be found elsewhere in this guide.

  1. Find the id of the menu to which you wish to add items. The simplest way to do this is to locate the menu_ids definition on the page template that renders the menu. Tables will have a single menu id. The page menu may have several, which will be rendered as submenus. The "TopLevel" menu is a special case; it appears in the page menu, but its items are rendered as siblings of the other menus.

  2. If activating the menu item will require a dialog, create one. See the "Dialogs" section of this guide for more info.

  3. Determine the objects for which the menu item should be visible. Menu items will use several criteria for determining whether to apply:

    • allowed_classes: A list of strings of class names for which the menu item should be rendered.

    • banned_classes: A list of strings of class names for which the menu item should not be rendered.

    • banned_ids: A list of strings of object ids for which the menu item should not be rendered.

    • isglobal: Whether the menu item should be inherited by children of the menu item's owner.

    • permissions: The permissions the current user must have for the context in order for the item to render.

  4. Figure out the action the menu item will perform. If it's a dialog, then the action is the name of the dialog template, and the isdialog attribute of the menu item should be True. If it's a regular link, the action should be the URL or "javascript:" you would normally have as the href attribute of an anchor.

  5. Now build the dictionary. It should look like this, where MenuId is the menu from step 1:

        menus = { 'MenuId': [
                    { 'id': 'myUniqueId',
                      'description': 'Perform My Action...',
                      'action': 'dialog_myAction',
                      'isdialog': True,
                      'allowed_classes': ('MyGoodClass',),
                      'banned_classes': ('MyBadClass',),
                      'banned_ids': ('Devices',),
                      'ordering': 50.0,
                      'permissions': (ZenossSecurity.ZEN_COMMON,)
                    },
                ]}

    'ordering' is a float determining the item's relative position in the menu. Greater numbers mean the item will be placed higher. Also notice that it's almost certainly pointless to set both 'allowed_classes' and 'banned_classes'; it was done here only as an example. The permission ZEN_COMMON is a standard Zenoss permission -- see the "Permissions" section of this guide for more information.

    If you have more menu items in the same menu, you can add them to that list; if you have more menus, you can create more keys in the menus dictionary.

  6. Finally, use the dmd.buildMenus method to create the MenuItems:

        dmd.buildMenus(menus)