By Thomas Rizzo
Microsoft Outlook provides a rich set of services that developers can take advantage of when building solutions. Some of these services include e-mail, calendaring, contact and task management, and the ability to customize Outlook forms using the built-in Outlook forms development environment.
This article focuses on one of those services, the Outlook development environment. The development environment provides a vehicle for rapid prototyping. It also allows for easy layout of a form's user interface, with the ability to create new fields and drag and drop these new fields onto the custom form.
It also allows developers to set the properties of controls on the form. For example, developers can make a specific control on a form read-only. Setting these properties on controls is global, however. This means that every user accessing the form will have the same permissions on the control. This article demonstrates how to implement hiding fields on a form in Outlook 98 by using the VBScript environment in Outlook forms. We'll examine the implementation needed to support this functionality and will look at a sample administration program for our implementation. We'll also demonstrate some new features in the Outlook 98 Object Model, and how to access databases from Outlook forms. Let's get started.
Deciding on an Approach
Why would you want to hide fields in an Outlook form? There are a number of scenarios that require this type of functionality. Imagine a custom Outlook job candidate tracking application. Users submit their resumes using an Outlook form. A representative in the human resources department opens the form and because they have permissions on specific fields on the form, they can see the salary information for this position and the candidate's Social Security Number, as shown in FIGURE 1. The human resources representative forwards the form to one of the candidate's interviewers for review. Since the interviewer does not have permissions on the salary or Social Security field, this information doesn't appear on the form (see FIGURE 2).
There are many ways a developer can implement custom security in their Outlook applications. One way is to create an ActiveX control and place the control on your Outlook form. However, this requires all your users to download and install the ActiveX control before they can use your application. Another way is to write directly to the Microsoft CryptoAPI and actual encrypt fields on the Outlook form, so only users who know the password to unencrypt the fields can access them.
This article will demonstrate how to use a database to store user permissions on Outlook controls. You can extend the implementation shown in this article with the previous two methods described.
Why use a database? A database is a logical place to store permission information and allows easy access to this information because most users have some type of data access components on their machine, whether it's ODBC, ADO, or DAO. A database also allows us to build relationships between tables in a database. This is powerful because these tables and their relationships can support referential integrity — a set of rules that a database follows to keep the relationships valid between tables in that database.
For example, a database can contain a relationship between a customers table and an orders table, where there are many orders for one customer. If referential integrity is enabled between these tables, a user cannot delete a customer if that customer has any pending orders in the orders table. Also, orders cannot be added to the orders tables unless there is a valid and associated customer in the customers table. FIGURE 3 shows an example from Microsoft Access of a one-to-many relationship with referential integrity enabled.
Architecture of the Application
When designing this application, there were four requirements that had to be met. First, it had to be easy for developers to reuse the code and easy enough for administrators to set up permissions for users. Second, it had to allow the administrator of the application to specify not only individual users access permissions, but also distribution list access permissions. This allows an administrator to group users and then assign permissions to the group. Third, an individual's permissions had to win out over any permissions that were different because the user was on a specific distribution list. For example, let's say that Jane is also part of a distribution list that doesn't have permissions on a specific control. If the administrator explicitly gives Jane permissions on that control, Jane's user permissions should be used instead of the distribution list's permissions.
Finally, the application had to be able to search multiple distribution lists for permission settings. If a user was on multiple distribution lists with different permissions, it had to resolve these differences and use the most optimistic permissions. For example, Jim is on two distribution lists, HR Users and HR Managers. HR Users has no permissions on the salary control. HR Managers, on the other hand, have write permissions on the salary control. Using optimistic permissions, Jim would have write permission on the salary control.
To meet these four requirements, the architecture for the custom security application included with this article is broken into two pieces. The first component of the application is an Outlook form that has some custom controls. These custom controls can be ListBoxes, CommandButtons, or any other intrinsic Outlook control. Furthermore, you can use this application in your own custom Outlook form by copying the VBScript functions into your custom form.
The second component of the application is the permissions database. The permissions database is a Microsoft Access database that holds the custom permissions for the controls on the Outlook form. The database schema for the permissions database is broken into three tables. The first table is a user table. This table contains the names of users who have permissions on specific controls. This table also contains a field called IsUserDL that tracks whether the username actually corresponds to a distribution list. The reason for this field is that many applications require that permissions are granted to groups of users instead of having to apply the permission to every user. The second table is the controls table. This table has a one-to-many relationship with the users table such that there can be many controls associated with a single user. The controls table is used to store the permissions for a user or distribution list on a specific control on a specific Outlook form. The final table is a temp table. This table is used for internal processing during the validation of permissions for a user when they access a form.
As described above, there are many different libraries to access databases. The code samples in this article use DAO as the access method to the permissions database. DAO ships as part of Microsoft Office 97, or Microsoft Visual Basic 5.0. You can easily customize the included sample to use any other data access library.
Diving into the Implementation
Before diving into the implementation, a brief introduction to the Outlook Object Model is necessary. The Outlook Object Model is the API that developers can use to write applications that take advantage of Outlook functionality. This object model can be called from within Outlook applications, or from other applications that automate Outlook and use its functionality. One example of using automation is creating new Outlook tasks from a Microsoft Word VBA application. The Outlook Object Model is an hierarchy of objects which stem from the top-level Application object, as shown in FIGURE 4. All other objects in the library can be accessed via the Application object.
When and how should permissions be checked in the form? The easiest way to set permissions on any Outlook form is to program the Item_Open event using VBScript. Microsoft Outlook fires this event on every form before the form is displayed. Also, since this event is a function, we can set the return value of it to False, which forces Outlook not to open the form. This can be useful when detecting errors in initialization code in your form. For example, in the custom security application, if DAO is not installed on the machine, the application displays a custom error message and sets the Item_Open function to False. This prevents users from opening the form when they do not have the correct environment and thereby bypassing the custom security. FIGURE 5 shows the application's Item_Open event handler, i.e. function.
As you can see in the Item_Open function, the application is divided into different routines. This is so you can take the application and paste it into your own Outlook application. Let's take a deeper look at the implementation of some of the main routines.
After some initialization routines, the application calls the OpenDatabase function (see FIGURE 6). This function creates the DAO object by calling the CreateObject method on the Outlook's application object. The function then opens the database from the specified user location. If any of these steps are unsuccessful, the Outlook form will not open since Item_Open will be set to False.
After opening the database, the ApplyUserPermissions function is called. This function queries the permissions database using DAO's OpenRecordset method. Some Outlook objects are used in the query. The first object that's passed is the current user's name. This is retrieved by using the Name method on the CurrentUser object. The current user's name is therefore dynamic; it's based on which user is accessing the form.
The second parameter is the message class of the form. Message classes in Outlook uniquely identify one form from another. Message classes also identify the form to use to bring up a specific item. For example, all contacts that you save in your Outlook contact folders have a message class of IPM.Contact. IPM stands for InterPersonal Message.
If you customize the standard contact form and publish your new form, you can create a new message class for your form. This message class will be based on the original items message class, but will have an extra identifier. Some example of custom forms and their custom message classes could be IPM.Contact.My Custom Contact, or IPM.Note.Expense Report.
Since the permissions database has specific information about a specific form, we need to pass the current message class for the form we're trying to open in the query. FIGURE 7 describes the query passed to the database.
The resulting recordset from the database contains the permissions for the current user for the controls on the Outlook form. The range of permissions in the application includes None/Not Visible, Read, and Write. These permissions are then applied to the form by first setting the correct tab where the control is located, and then setting the correct properties for that control. Later in this article, we'll see another permission for setting the administrator permissions for an Outlook form. The properties that are set on the controls to obtain these permissions are Enabled and Visible. For example, if a control is set to read-only, the application sets the Visible property to True and the Enabled property to False.
After setting the permissions on the control, a history log of the set permissions is written to a temporary table. This is to maintain a record of the permissions already set. Since user permissions "win out" over distribution-list permissions, a special flag, called IsUserDefined is set in the temporary table. This flag tells the application that even if a distribution list has different permissions on the same control, the application should ignore those permissions.
The next function called is ApplyDLPermissions. (The ApplyDLPermissions function is shown in Listing One beginning on page XX. The entire demonstration project, including Outlook template and Access database, is available for download; see end of article for details.) This function uses some new objects that are only available in the Outlook 98 object model. These new objects are the AddressLists and the AddressEntries Collections. The AddressLists Collection is the parent object of the AddressEntries Collection. This makes sense since AddressLists, such as your personal address book, contains entries for other users and their relevant information.
The AddressLists Collection can access address books in Outlook 98. These address books can include personal address books, global address books, Outlook address books (Outlook Contact folders), and even address books which are replicated and used when offline, such as the offline global address book. By passing a name of an address book to the AddressList object, a user can search through the entries in that address book. In the custom security application, the name of the address book is customizable in the VBScript code.
After retrieving the AddressList object, the application obtains the address entry objects for the specified address book. Since permissions can be assigned to distribution lists, the application queries the permissions database for all distribution lists with permissions on the current Outlook form. The application then checks all the distribution lists to see if the current user is a member of the distribution list. If the current user is a member, the application checks the temporary table to see if permissions have already been assigned for the form. If they have, the application resolves the conflict by saving the most optimistic permissions. After the application is finished searching the distribution lists, it then commits all the permissions stored in the temporary table to the form.
The final task of the application is to clean up the temporary table. To do this, the application calls the Execute command of the DAO database object with a SQL DELETE statement.
Sub ClearTempDatabase
strDeleteTemp = "DELETE Temp.* FROM Temp"
MyDB.Execute strDeleteTemp
End Sub
The form now has the correct permissions set, and is ready to be used by the current user.
Some Implementation Suggestions
When using the application in this article, there are some specific settings on your Outlook form, and in the database, that further enhance the custom security. For example, you'll probably want to password-protect the design of your Outlook form so users cannot change the design or modify the script behind the form. To enable password protection, go into the design mode for your form and select Protect Form Design on the Properties page.
Also, make sure to set the permissions on the permissions database so end users cannot modify the database directly. Finally, make all sensitive fields on the Outlook form not visible and not enabled by default. If the user is supposed to have permissions on that control, the application will make the control visible and/or enabled.
If you're interested in learning more about programming using the Outlook object model, please visit http://www.microsoft.com/outlook, or search the Outlook CD, or the Microsoft Web site for the Outlook forms help file named olform.hlp.
Download source code for this article here.
Thomas Rizzo works as a Product Manager in the Microsoft Exchange Server Product Unit in Redmond, WA. He specializes in evangelizing development features in both Exchange and Outlook. You can reach Tom at thomriz@microsoft.com.