dBASE Menu Configuration
(This worked for me)
by Steve Hawkins

March 2005 - The application I was designing required that each user be given access to certain menu functionality in accordance with their assigned "user level". It was determined that users should not see menu choices that they would not be able to use. For that reason, simply disabling menu choices was not an option. Additionally, there are a few "super users/admins" that wanted/needed a menu structure that was completely different from most other users. I needed to customize from two angles. First, I needed to pick the desired menu structure (.MNU file) for each user. Second, once the correct menu structure was selected, I needed to display or hide menu choices based on user level.

The following outlines my approach to resolving this problem. I would like to express my gratitude to all those on the dBASE newsgroups who assisted in this endeavor. In particular, I would like to acknowledge Jim Sare. His answer to one of my questions about menus pushed me over the hump, allowing me all the flexibility I needed in customizing my application's menus at program startup.

Please note: My START.PRG has functionality that is specific to this particular application. I'm excluding that which does not somehow relate to the issue of menu configuration. I don't present the complete .PRG file and this code won't run on its own. My only goal here is to discuss my approach to menu configuration.


I use a program file (START.PRG) to open my application. Use of START.PRG is outlined in the excellent (and free) dBASE tutorial. I'm going to assume that you have read or plan to read the tutorial and will refrain from expounding further on that topic.

When START.PRG opens, the first thing it does is acquire the user id. The PRG then calls its OPEN() method. The OPEN() method starts out with the following:

_app.lOktoRun = StartupCheck() // in CONFIG.PRG

The StartupCheck() code resides in my CONFIG.PRG which in turn contains lots of startup, configuration and validation code, etc. Among the things that take place, the StartupCheck() method/function validates that the user id exists in the user table. If so, it acquires the user name and user level from fields in that table. It store these values to _app variables (see "_app.userlevel" below for example).

If the user can't be found, or some other "disaster" occurs, _app.lOktoRun will be false. Otherwise, we have a green light and the following code is processed:

   IF _app.lOktoRun  // if not, something crapped out - app will close
   	// set a reference to the menu
   	private c

      // Set menu name based on user level
      if _app.userlevel > 2  // System admin user
      	this.menuclassname = "mspdlog3menu"
      else
        this.menuclassname = "mspdlogmenu"
      endif

      /* NOTE:  The menu "mspdlogmenu.mnu" contains If/Endif constructs
      	using _app.userlevel.  It constructs some top level menu
        items ONLY if the userlevel is sufficiently high (>1).  See below  */

      //build the menu command (a 'macro'):
      c = 'this.rootMenu = new '+this.MenuClassName+;
          '(_app.framewin,"Root")'
      // execute it:
      &c

   ELSE	// Something is not right (user will know what)
   	close databases
   	super::close()  // close the application
   ENDIF

If you examine the code above, you can see that administrative users (level 3 or above) see a different menu structure ("mspdlog3menu") than those with a lower level (level 1 or 2 get the "mspdlogmenu"). The contents of the menu for admin users is not really relevant so I won't list it here. However, the menu for level 1 and 2 users is a bit different than most. I designed this menu for the level 2 user. There is a top line menu item though ("Utilities") that is not supposed to be seen by level 1 users.

Once the level 2 menu was designed using the menu designer, I opened it in the source code editor and changed it manually. The process was quite simple. I placed the constructor code for the Utilities menu inside an "IF/ENDIF" (see code below).

//The following is part of the constructor code for
//mspdlogmenu.mnu
if val(_app.userlevel) > 1	// power user
   this.UTILMENU = new MENU(this)
   with (this.UTILMENU)
      text = "&Utilities"
   endwith

   this.UTILMENU.MENU7 = new MENU(this.UTILMENU)
   with (this.UTILMENU.MENU7)
      text = "&Officer List"
   endwith

   this.UTILMENU.MENU8 = new MENU(this.UTILMENU)
   with (this.UTILMENU.MENU8)
      text = "&Category List"
   endwith

   this.UTILMENU.MENU9 = new MENU(this.UTILMENU)
   with (this.UTILMENU.MENU9)
      text = "&Posts"
   endwith
endif

That's all there is to it. If a user has a level 2 status, they see the Utilities menu. If they are level 1, this menu item never appears. If they have a level 3 or higher status, they see a completely different menu structure. How simple is that?

With this knowledge under my belt, I can now implement any number of menu combinations as the need arises. Combining this capability with dBASE's DEO functionality provides for a very powerful and robust means of giving users the menu structure they need. My users recently told me they needed a small change to the level 3 menu. Five minutes later, I sent them a small .MNO file and told them to copy it to their designated DEO folder. All done. Very cool. Very happy users.

I hope these notes are useful to you. If you have any questions or comments about this text, please feel free to email me at the address below. If you have general dBASE questions, please visit the dBASE newsgroups.

Email address:
webemail.gif (1K)