Day 17

Reusable JavaScript Components

by Ted Graham


CONTENTS

Over the last 16 days, you have learned how to use the IntraBuilder design tools, and you have built and deployed an IntraBuilder application. Even though you have the skills necessary to create Web-based applications, there is no need for you to build every part of these applications from scratch. So, today, you begin exploring the world of reusable components and how to integrate them into an IntraBuilder application.

Today you will learn about the following topics:

Types of Components

Several different items fall into the category of reusable JavaScript components. JavaScript components can be either visual components or non-visual components.

Visual components are custom classes based on IntraBuilder's standard visual classes, such as Form, HTML, or Button.

Non-visual components are custom functions or custom classes based on non-visual IntraBuilder classes, such as Object or Date.

Visual Components

In addition to the standard user interface components such as buttons and images, IntraBuilder ships with a wide variety of custom user interface components. Based on standard IntraBuilder components, these components are customized to meet specific needs.

Several custom component files ship with IntraBuilder. Two of them reside in the Custom folder. These are Expert.cc and ActiveX.cc. The third one, Controls.cc, resides in the Apps\Shared folder. The prebuilt business solutions use the Controls.cc file, and you can use it for your own applications as well. The last two files, Chart.cc and Counter.cc, reside in the Samples folder.

Loading Custom Components

Custom components are typically stored in a JavaScript source file with a .cc extension. After you load the classes defined in the .cc file into memory, they appear in the Custom tab of the Component Palette.

You can load a .cc file into memory in two different ways. The first way is to locate the file using the IntraBuilder Explorer. The .cc files appear in the Custom tab of the IntraBuilder Explorer, as shown in Figure 17.1. Double-clicking the .cc file loads the file into memory for the duration of the current IntraBuilder session.

Figure 17.1 : The Custom tab of the IntraBuilder Explorer showing custom component files.

You can also configure IntraBuilder to load the .cc file every time the IntraBuilder Designer is loaded. In fact, the installation program has already configured the IntraBuilder Designer to load the Expert.cc file. You can add and delete .cc files from the process while in the Form Designer. To add a .cc file to the list of files, follow these steps:

  1. Open the Form Designer.
  2. Select File|Set Up Custom Components.
  3. Press the Add button and select the custom component file.
  4. Press OK to close the Set Up Custom Components dialog.

NOTE
Sometimes the components do not appear right away on the Component Palette. A quick way to get the palette to refresh is to right-click the palette and select the Toolbars and Palettes option. Then change the Component Palette's Show Tabs setting and press OK. This should update the palette, showing the new components. You can follow the same steps to change the tabs back if you want.

Expert.cc

The Expert.cc file contains classes that duplicate the image and button components generated by the Form Expert. As shown in Figure 17.2, the Form Expert can generate either buttons or images to perform many common table operations such as navigating, adding records, and performing searches.

Figure 17.2 : The Form Expert.

The same images and buttons can be added to your forms using the custom components in Expert.cc. The expert components appear on the Component Palette on two separate tabs: Update and Navigation. Figure 17.3 shows the Component Palette with some of the components from Expert.cc in the Update tab.

Figure 17.3 : Custom components in the Component Palette.

A few special notes should be made about these components. All of the components in Expert.cc require that the form's rowset property be set. This is the only way for the component to know what table to update or navigate. If you initially created the form using the Form Expert, the rowset property has already been set for you. If you create the form from scratch, you need to set the property yourself. You can do this by right-clicking on the form and then selecting the Inspector option on the shortcut menu. You then press the drop-down button next to the rowset property to open a list of all the open queries. Select the query that you want the components to manipulate. You can select only one of the available queries.

You should also be aware that the image components copy a small image file into the current folder when they are placed onto a form. This image file is copied from the Designer\Form folder. If the form is later moved to a new location, you should be sure to copy these image files as well.

Update Buttons

The components shown in Figure 17.3 are update buttons and update images. They are shown with pictures in the Component Palette to give you an idea what the components do. These components are also shown in Table 17.1 along with the class name that they represent and a brief description of what the buttons do.

Table 17.1. The update buttons in Expert.cc.

IconText Class NameDescription

Add AddButtonBegin appending a new record.

Delete DeleteButtonDelete the current record.

Edit EditButtonBegin editing the current record.

Save SaveButtonSave changes to the current record.

Abandon AbandonButtonAbandon changes to the current record.

New Query SearchButtonEnter search mode/submit search.

New Filter FilterButtonEnter filter mode/submit filter.

You use these update buttons in the Form Designer just like you would use a standard IntraBuilder button. You just drag the button from the Component Palette to the form and place it wherever you choose. The benefit of using these custom components is that the JavaScript necessary to perform the action is already attached to the onServerClick event. However, you are free to modify any of the properties of the component, even the text or onServerClick.

The purpose of most of these buttons should be obvious, but a few might not be quite so obvious, such as the EditButton class. This button is unnecessary if the query's rowset.autoEdit property is left to its default value. When left to the default value of true, the rowset data displayed in the form can be updated automatically. However, when the value is false, attempts to update the data are ignored. Before you can update the data, you must call the rowset.beginEdit() method. After calling this method, you can update the data for the current record. The purpose of the EditButton class is to call this method to allow you to begin editing the current record.

The SearchButton and FilterButton classes are both two-state buttons. When you first press these buttons, they begin the search or filter operation. At this point, the text on the button changes to "Run Query" or "Run Filter." While it is in the second state, the button is used to apply the criteria that were entered during the search or filter mode and to change the text back to the original text.

Update Images

The remaining components shown in Figure 17.3 are all custom image components. They are shown with pictures in the palette to give you an idea of what the components do. These components are also shown in Table 17.2, along with the class name that they represent and a brief description of what the images do.

Table 17.2. The update images in Expert.cc.

IconClass Name Image FileDescription

AddImage UPDADD.gifBegin appending a new record.

DeleteImage UPDDEL.gifDelete the current record.

EditImage UPDEDIT.gifBegin editing the current record.

SaveImage UPDSAVE.gifSave changes to the current record.

AbandonImage UPDABAN.gifAbandon changes to the current record.

SearchImage SRCHQBF.gifEnter search mode/run search.

FilterImage FLTRFBF.gifEnter filter mode/run filter.

UpdateHorizontal Image EXPIROWH.gifAll seven images aligned horizontally.

UpdateVertical Image EXPIROWV.gifAll seven images aligned vertically.

The seven images corresponding to the update buttons perform the same function as their button counterparts. Likewise, the two composite images perform the same functions as the individual images. The only difference between the images and the buttons is the absence of any text. This has the potential to confuse users, particularly when switching between the two states of the SearchImage and FilterImage classes. You might want to include some other visual cues about the current state of the form when using the image components.

Navigation Components

In addition to the update components, Expert.cc also has navigation components. Like the update components shown in Figure 17.3, there are both navigation buttons and navigation images. Tables 17.3 and 17.4 show the palette icons, the text (only for the buttons), the class name, the image file (only for images), and the purpose of the navigation components.

Table 17.3. The navigation buttons in Expert.cc.

IconText Class NameDescription

First FirstButtonMove to first record.

Previous PreviousButtonMove to previous record.

Next NextButtonMove to next record.

Last LastButtonMove to last record.

Table 17.4. The navigation images in Expert.cc.

IconClass Name Image FileDescription

FirstImage NAVFIRST.gifMove to first record.

PreviousImage NAVPREV.gifMove to previous record.

NextImage NAVNEXT.gifMove to next record.

LastImage NAVLAST.gifMove to last record.

NavigateHorizontal Image EXPINAVH.gifAll four images aligned horizontally.

NavigateVertical Image EXPINAVV.gifAll four images aligned vertically.

The purpose for each of the navigation buttons and images should be quite clear. Each button has a corresponding image that performs the same action. The NavigateHorizontalImage and NavigateVerticalImage components are particularly useful because of the amount of capability that is packed into such a small amount of screen space.

ActiveX.cc

The ActiveX.cc file contains a collection of custom ActiveX components. This file is also located in the Custom folder; however, it is not automatically loaded when IntraBuilder begins.

The 12 custom components are used just like regular ActiveX components. You place the component on a form and then set the params property values to control the component's behavior.

These ActiveX components all ship with Microsoft Internet Explorer 3.0. Because they are all part of Internet Explorer, you do not have to set the codeBase property of the component. This makes these components very easy to use. Complete documentation for these components can be found on the Microsoft Web site. At the time of printing, this information was contained at the following address:


http://www.microsoft.com/intdev/controls/ctrlref.htm

The following components are contained in ActiveX.cc:

Controls.cc

The Controls.cc file contains a variety of custom components. These were originally created for use in the prebuilt business solution applications that ship with IntraBuilder. This file is located in the Apps\Shared folder.

File Lists

There are two file list components. One is a custom Select component named FileSelect, and the other is a custom ListBox component named FileListBox. Both of these components are used in the same way.

Both Select and ListBox components take their options (the items that appear in the list) from an array. The Form Designer has an array builder tool for creating static arrays, but there are no visual tools to get the filenames from a folder into such an array. You would normally have to write some JavaScript code to read and store the filenames in an array.

The file list components are designed to do this work for you. The components have the same properties and methods of the standard components that they are based on. In addition, each component has one custom method.

To use the component, follow these steps:

  1. Load Controls.cc as described earlier.
  2. Place the FileSelect or FileListBox component on the form.
  3. Right-click the component and select Inspector.
  4. The options property currently contains
    array{"*.*"}
    Replace the *.* with the template for the files that you want shown in the list. For instance, to show a list of reports in the current folder, change the options property to read as follows:
    array{"*.jrp"}
    Figure 17.4 shows the Inspector after making this change.
    Figure 17.4 : Inspecting the FileSelect object.


    You can use the * wildcard character to represent any number of characters, and you can use the ? wildcard character to represent any single character. You can optionally include a path, such as this one:
    array{"c:\\Borland\\IntraBuilder\\Apps\\*.gif"}

  5. Set any additional properties or events for the component.

When the form is opened, the control automatically generates the list of options based on the file specification that you provided. This list is built only once, when the form is first opened. If you want to rebuild the list while the form is open, you call the custom fillOptions() method. This method regenerates the list based on the original file specification.

Field Lists

The two field list components are a custom Select component named FieldSelect, and a custom ListBox component named FieldListBox. Both of these components are used in the same way.

Like the file list components, the field list components are designed to create the options array for you. When you provide a table name and a field name, the field list component fills the options array with data from that field.

To use the component, follow these steps:

  1. Load Controls.cc as described earlier in the "Loading Custom Components" section.
  2. Place the FieldSelect or FieldListBox component on the form.
  3. Right-click the component and select Inspector.
  4. The options property currently contains
    array{'table','field'}
    Replace the word table with the name of any actual table. Likewise, replace the word field with the name of a field in that table. For instance, to fill a list with the names of products from a product table, you would set the options property as follows:
    array{'product.db','Product Name'}
  5. Set any additional properties or events for the component.

NOTE
The field list components only need the name of the table and the field. In order for the component to find this table, there must be a default query on the form, and the table must be in the same folder or database as the default query. If you use the Form Expert to create the form, then the expert created a default query. If you create the form from scratch, you need to add a query object yourself, and then set the form's rowset property to point to the query object.

When the form is opened, the control automatically generates the list of options based on the specification that you provided. This list is built only once, when the form is first opened. If you want to rebuild the list while the form is open, you call the custom fillOptions() method. This method regenerates the list based on the original specification.

The IntraBuilder Phone Book application uses a field list component in the Update form. View the source script for the Update form to see an example of using the fillOptions() method. The Phone Book application comes with IntraBuilder and can be found in the Apps\Phone folder.

Back Buttons

Two different buttons in Controls.cc can move you back a page. The first is the ClientBackButton component, and it uses client-side JavaScript. Clicking this button in a browser is the same as pressing the browser's own back button.

The second component is ServerBackButton. This component's action takes place on the server. This button closes the current form. IntraBuilder then re-renders the last form that was opened before the current form opened. This component acts as an application back more so than the browser back. For example, you use a browser to open a home page that was created with the IntraBuilder Home Page Expert. From that form, you select a second IntraBuilder form that contains customer data. You press the Next button a few times and then press the Back button. If the button performs a client-side back, you view the previous customer record (the last page viewed in the browser). If the back button performs a server-side action to close the customer form, you return immediately to the home page (the last IntraBuilder form that had focus).

An application back is a button or image that looks like a browser back button but submits the form to the IntraBuilder Server rather than retrieving the previous browser page from the browser cache. An application back button sends a new page to the browser that looks like the previous page but might have some updated information. A browser back button simply retrieves the previous page from a cache without making any modifications.

HTML Components

There are two HTML components in Controls.cc. The first is just like any standard HTML component except that the alignment properties have been set to center the text. This component is appropriately named CenterHTML.

The second HTML component is called GeneratedHTML. This component is used at the bottom of the prebuilt business solution application forms to indicate the date and time that IntraBuilder generated the HTML stream that is sent to the browser. Figure 17.5 shows a simple form with a GeneratedHTML component at the bottom.

Figure 17.5 : A browser showing the GeneratedHTML component.

Chart.cc

The Chart.cc file resides in the Samples folder. It contains two charting components. These two components are used to display data from a table in the form of a chart. The two components are named as follows:

Both of these classes are used in the same way. After placing the components on the form, you need to set the form's onServerLoad() method to call the component's Init() method. You need to pass the Init() method three pieces of information that the component uses to build the chart. The first is the name of the field that is used to identify each piece of data. This field is used as the labels on the chart. The second is the name of the field that contains the data to be charted. The third is an object reference to the rowset object that contains the data.

The table used for the chart must already be open by the time you call the Init() method. The sql property for the Query object should take care of grouping the data and calculating the total for each group.

The sample files ActiveX.jfm and Java.jfm (also in the Samples folder) demonstrate the use of these two components. Listing 17.1 shows the contents of ActiveX.jfm, using the ActiveXChartQuery component.


Listing 17.1. The contents of ActiveX.jfm.

 1: // {End Header} Do not remove this comment//

 2: // Generated on 02/14/97

 3: //

 4: var f = new activexForm();

 5: f.open();

 6: class activexForm extends Form {

 7:   _sys.scripts.load("chart.cc")

 8:    with (this) {

 9:       onServerLoad = class::form_onServerLoad;

10:       height = 14.6667;

11:       left = 0;

12:       top = 0;

13:       width = 76;

14:       title = "ActiveX Sample";

15:    }

16:    with (this.query1 = new Query()){

17:       left = 34;

18:       top = 8;

19:       sql = "select c.state_prov, sum(o.total) as sumTotal from " + 

20:             "customer c, orders o where c.customer_n = o.customer_n " +

21:             "group by c.state_prov";

22:       active = true;

23:    }

24:    with (this.query1.rowset) {

25:    }

26:    with (this.ActiveXChartQuery1 = new ActiveXChartQuery(this)){

27:       height = 13;

28:       top = 1.5;

29:       width = 74;

30:       params["ChartType"] = "8";

31:    }

32:    with (this.html1 = new HTML(this)){

33:       height = 1.5;

34:       width = 74;

35:       color = "black";

36:       alignHorizontal = 1;

37:       text = "Total Sales by State (in U.S. Dollars)";

38:    }

39:    with (this.html2 = new HTML(this)){

40:       height = 1;

41:       top = 14.5;

42:       width = 74;

43:       color = "black";

44:       alignHorizontal = 1;

45:       text = "(Data computed from customer.dbf and orders.dbf.)";

46:    }

47:    with (this.button1 = new Button(this)){

48:       onClick = class::button1_onClick;

49:       left = 18;

50:       top = 16;

51:       width = 10.5;

52:       text = "Back";

53:    }

54:    with (this.switchViewButton = new Button(this)){

55:       onClick = class::switchViewButton_onClick;

56:       left = 42;

57:       top = 16;

58:       width = 16;

59:       text = "Switch View";

60:    }

61:    this.rowset = this.query1.rowset;

62:

63:    function form_onServerLoad()

64:    {

65:       this.ActiveXChartQuery1.Init( "state_prov", "sumTotal",

66:          this.query1.rowset ) ;

67:    }

68:

69:    function button1_onClick()

70    {

71:       history.back();

72:    }

73:

74:    function switchViewButton_onClick()

75:    {

76:       var ChartType = document.forms[0].ActiveXChartQuery1.ChartType;

77:       if (ChartType == 5)

78:          ChartType = 8;

79:       else if (ChartType == 8)

80:          ChartType = 14;

81:       else

82:          ChartType = 5;

83:       document.forms[0].ActiveXChartQuery1.ChartType = ChartType;

84:    }

85: }


Lines 16 to 23 define the query object, which contains the data to be charted. Lines 19 through 21 contain the SQL statement that retrieves the state names and the total sales for each state. This query's rowset is used later in the form_onServerLoad() method that begins on line 63.

Lines 26 to 31 define the chart component itself. The definition of the chart component is very simple. The actual initialization of the component is done in the form_onServerLoad() method. Line 65 calls the Init() method of the component.

Counter.cc

The Counter.cc file is also in the Samples folder. This contains a single custom component named Counter. This component is subclassed from the HTML component and has all the same properties and methods as a standard HTML component.

The Counter component is used to keep track of the number of times that an IntraBuilder Web page is visited, or hit. You place the component onto the form just like you would a normal HTML component. You can then set the visual properties, such as the color or font properties. You do not need to set any other properties at this time.

Here is an example from the SampHome.jfm file, which uses the Counter component:


with (this.Counter1 = new Counter(this)){

   height = 1.1667;

   top = 28;

   width = 70;

   color = "blue";

   fontBold = true;

}

Notice that the Counter component's definition is very simple. This is because the custom properties are set in the form's onServerLoad() event handler. Four custom properties need to be set. The following lines show each of these properties being set:


form.Counter1.cTableName= "COUNTERS"

form.Counter1.cCountName= "SAMPHOME"

form.Counter1.cFontSize = "5"

form.Counter1.cText     = "Number of visits to this page: "

Again, this code comes from the SampHome.jfm file. The cTableName property identifies a table that is used to keep track of the number of times this form is opened. If the table does not exist, it is created automatically. The same table can keep track of hits to more than one page. The cCountName property must uniquely identify this page within the table. The cText property is displayed to the left of the actual hit count, and the cFontSize property is the size of the count itself.

Non-visual Components

In addition to the many visual custom components that ship with IntraBuilder, there are also many non-visual custom components. These components take several forms, such as header files, functions, and classes that you can reuse in your own scripts.

Header Files

Several header files are in the Include folder. These header files can be included in your own script files to simplify your programming tasks. Two of these header files, Intra.h and Windef.h, are described here. The rest are used in conjunction with custom components.

Using Header Files

If you are already familiar with the use of header files and the #include preprocessor directive, you might want to go on to the next section. If you have not worked with header files before, this section describes what header files are and how to use them in your JavaScript source files.

In many programming languages, header files are used to store preprocessor directives, type declarations, function prototypes, and simple macros. A header file usually ends with a .h suffix, as in Intra.h. The header file is included in a source file by using the #include preprocessor directive. For instance, the following line includes file Intra.h in the current JavaScript file:


#include "intra.h"

The #include directive is handled by the preprocessor, which prepares the source code for compilation. Before compiling a JavaScript source file, the preprocessor goes through the source and executes any preprocessor commands. These commands are used to make changes to the source code "on the fly." The resulting source code is then sent to the compiler. You used preprocessor directives in yesterday's lesson when you included debug code in your scripts. Consider this script:


#define DEBUG

#define DEBUG_TEXT "In Debug Mode"

#ifdef DEBUG

alert( DEBUG_TEXT );

#endif

Before this script is executed, it must pass through the preprocessor and then the compiler. The preprocessor handles the lines beginning with the # symbols. After the preprocessor has processed the script, it simply looks like this:


alert( "In Debug Mode" );

This is the only command that is actually sent to the compiler. Notice that the DEBUG_TEXT identifier has been replaced by the string "In Debug Mode" and that the other preprocessor commands are no longer present.

This very brief explanation tries to get across the idea that the preprocessor generates a copy of the source file that has been reshaped based on the various preprocessor directives contained in the code.

The #include directive is used to instruct the preprocessor to insert the specified file into the source code at the current location. When the compiler gets the resulting file, it appears as a single source file. When the header file has been included in your source file, you can reference any of the identifiers that have been declared.

When you #include a file, the preprocessor looks for the included file in the current folder. If it is not found, the preprocessor looks in the Include folder for the file.

IntraBuilder Constants

IntraBuilder classes include many enumerated properties. For instance, the rowset class contains the state property, which contains a numeric value from zero to five. Each of these numeric values indicates that the rowset object is in a different state. For example, 3 indicates that the rowset is in append mode. The following code sets the text of an HTML component based on the rowset's state. It can be used in a form's preRender event.


if (this.rowset.state == 3)

   this.stateHTML.text = "Appending";

else

   this.stateHTML.text = "Editing";

It can be very difficult to remember what all the state values represent and even more difficult to remember what all the different enumerated property values mean. The Intra.h file helps to eliminate this problem by defining constants with more recognizable names for each of these cryptic numeric values. For example, the same code can be rewritten like this:


#include "intra.h"

if (this.rowset.state == STATE_APPEND)

   this.stateHTML.text = "Appending";

else

   this.stateHTML.text = "Editing";

This version of the code is much easier to understand when you go back to make changes later. Because it is much easier to remember descriptive identifiers such as STATE_APPEND than to remember numeric values, you might also find it much easier to write your scripts using this header file.

The constants defined in Intra.h follow the same basic naming convention. The constants start with the name of a property, such as STATE_, followed by a descriptive identifier, such as APPEND. The exceptions to this rule are the PRINTER_COLOR_ constants, which are used for the color property of Printer objects.

Windows API Data Types

Another header file in the Include folder is Windef.h. This header file defines preprocessor identifiers for the most common data types used by the Windows API. IntraBuilder allows you to use external functions (including Windows API functions) by use of the extern command. This command requires you to identify the data type used for each function parameter and the return value. Normally, you would identify the data type using one of the IntraBuilder type identifiers, such as int or char*.

The Windows API defines many custom data types, which are variations of the standard data types. The many API references available usually give the Windows data types while describing the prototype of the function. This requires you to convert from the Windows data type to the base data type in order to prototype the function in IntraBuilder. However, Windef.h makes this job easier by defining many of the Windows data types for you.

NOTE
To see an example of accessing the Windows API and of using the Windef.h header file, take a look at the Registry.js file in the Apps\Shared folder. This file contains the definition of a Registry class that is used by the solution applications that come with IntraBuilder.

The Registry Class

One of the most useful components that ships with IntraBuilder is the Registry class. This class reads and writes values to the Windows registry. This can be a very useful way of storing configuration information for a JavaScript application.

The Registry class is defined in the file Registry.js in the Apps\Shared folder. In order to use the Registry class, you must load this file into memory so that the class definition is available to IntraBuilder. In addition to loading this file into memory, you need to #include the file Winreg.h. This header file defines several constants that you need as you work with the Registry class.

Using the Registry Class

Using the Registry class, your scripts can read information directly from the system registry, as well as write data back to the registry. The following script demonstrates creating a Registry object:


#include "winreg.h"

_sys.scripts.load(_sys.env.home() + "apps\\shared\\registry.js");

var reg = new Registry(HKEY_LOCAL_MACHINE,"Software\\MyCompany");

The object that is created by new Registry contains a reference to a single key in the registry. That key is identified by the two parameters passed to the Registry class. The actual syntax for the registry class is as follows:


new Registry( <open key> , <sub-key> )

The <open key> parameter takes a Windows handle to an open registry key. The Winreg.h header file provides identifiers for the root keys, which are always open. You must use one of these identifiers, which are defined in Winreg.h:

The <sub-key> is a character string identifying a subkey of the <open key>. This parameter is required, but it can be an empty string. In the previous code example, the Registry object references the HKEY_LOCAL_MACHINE\Software\MyCompany key.

Each Registry object can reference only a single key in the system registry. When you read from and write to multiple keys, you need to create multiple Registry objects.

Registry Properties and Methods

After you have created the Registry object, you can use any of the object's properties and methods. The object contains two properties-newlyCreated and error. When you create a new Registry object, the specified key is opened. If it does not already exist, it is created and then opened. If it was necessary to create the key, the newlyCreated property is set to true; otherwise, it is set to false. If any error occurs when trying to open or create the key, the error property is set. The error property contains the Windows error message that prevented the key from being opened or created. Each of the methods described in the following list can also set the error property. You might want to check its value after each call to one of the Registry methods.

The object also has five methods, which are shown in the following list along with their parameters:

The deleteValue() method deletes the named value. If <name> contains an empty string, the default value of the key is deleted. The method returns a Boolean value indicating whether or not the delete was successful.

The enumValue() method returns an array containing the name of each value contained in the current key.

The queryKeyName() method returns a string containing the name of the referenced registry key.

The queryValue() method takes a string that identifies a value contained in the current key. If <name> contains an empty string, the key's default value is returned. If <name> contains a string, the data associated with that named value is returned. The data type of the returned value depends on the data type of the value in the registry. Most registry entries are strings, including most of the numeric entries. However, some registry entries are actually identified as numbers. In such a case, the return value from queryValue() is actually an IntraBuilder numeric value.

The setValue() method takes three parameters. Only the third one is optional. The <name> property identifies the named value to set. This can contain an empty string to set the default value. The <data> parameter is generally a string value. This <data> is stored in the registry and identified by <name>. If the <type> parameter is omitted, the <data> is considered to be of a string type. To store the <data> in a different format, you must pass the <type> parameter as well. The Registry class currently supports only the two most common registry types-REG_SZ for strings, and REG_DWORD for four byte integer values. These data type identifiers are defined in Winreg.h.

Be very careful when writing to the system registry. The operating system and many software packages keep their configuration information in the registry alone. If you inadvertently overwrite one of these configuration settings, the operating system or one of the software packages might stop working.

To see an example of using the Registry class, check out the file Server.jfm in the Server folder. (Note that this was in the Samples folder in the 1.0 release.) A portion of this file is shown in Listing 17.2.


Listing 17.2. Using the Registry class in Server.jfm.

 1: #include "WINREG.H"

 2: #define SERVER_REG_KEY  "SOFTWARE\\Borland\\IntraBuilder\\1.0"

 3: function form_onServerLoad()

 4: {

 5:    // load the registry class library

 6:    _sys.scripts.load("registry.js");

 7:    // load the registry values

 8:    form.registry = new Registry(HKEY_LOCAL_MACHINE, SERVER_REG_KEY +

 9:       "\\Server" );

10:    form.remoteAgents = new Array();

11:    form.maxAgentID = -1;

12:    if (form.registry.error == 0) {

13:       form.pathText.value = 

14:          form.resetPath = form.registry.queryValue("IntraPath");

15:       form.instancesText.value = 

16:          form.resetInstances = form.registry.queryValue("Agents");

17:       form.sessionsText.value = form.resetSessions =

18:          form.registry.queryValue("MaxSessions");

19:       form.timeoutText.value = form.resetTimeout =

20:          form.registry.queryValue("Timeout");

21:       // load remote agent information

22:       form.readRemoteAgents(form);

23:    }

24:    else {

25:       form.pathText.value      = "Error reading system registry";

26:       form.instancesText.value = "Error";

27:       form.sessionsText.value  = "Error";

28:       form.timeoutText.value   = "Error";

29:       form.saveButton.visible  = false;

30:       // disable remote agent button

31:       this.remoteAgentsButton.visible = false;

32:    }

33: }

34: function readRemoteAgents(oForm)

35: {

36:    oForm.remoteAgents = new Array();

37:    oForm.maxAgentID = -1;

38:    var nAgentId = -1;

39:    var aValues = oForm.registry.enumValue();

40:    aValues.sort();

41:    for (var i = 0; i < aValues.length; i++)

42:       if (aValues[i].substring(0,6).toUpperCase() == 'REMOTE') {

43:          nAgentID = aValues[i].substring(6,aValues[i].length)

44:          oForm.remoteAgents.add(nAgentID + ", " +

45:             oForm.registry.queryValue(aValues[i]));

46:          if (parseInt(nAgentID) > oForm.maxAgentID)

47:             oForm.maxAgentID = parseInt(nAgentID);

48:       }

49:    oForm.remoteAgentsListBox.options = "array form.remoteAgents";

50:    if (oForm.remoteAgents.length == 0) {

51:       oForm.updateButton.visible = false;

52:       oForm.deleteButton.visible = false;

53:    }

54:    else {

55:       oForm.updateButton.visible = true;

56:       oForm.deleteButton.visible = true;

57:    }

58: }

59: function saveButton_onServerClick()

60: {

61:    this.form.registry.setValue("IntraPath",form.pathText.value);

62:    this.form.registry.setValue("Agents",form.instancesText.value);

63:    this.form.registry.setValue("MaxSessions",form.sessionsText.value);

64:    this.form.registry.setValue("Timeout",form.timeoutText.value);

65: }

66: function deleteButton_onServerClick()

67: {

68:    var agent = this.form.remoteAgentsListBox.value;

69:    if (agent.length > 0) {

70:       this.form.registry.deleteValue("Remote" + 

71:          agent.substring(0,agent.indexOf(",")));

72:       this.form.readRemoteAgents(this.form);

73:    }

74: }

75: function saveAgentButton_onServerClick()

76: {

77:    this.form.registry.setValue("Remote" + this.form.agentIDText.value,

78:        this.form.userNameText.value + "@" + this.form.machineNameText.value);

79:    this.form.showAgentControls(this.form, false);

80:    this.form.readRemoteAgents(this.form);

81: }


Line 1 uses the preprocessor to include the registry constants used by the script. Line 2 defines the base registry key name. This is used on line 8 to create the registry object.


TIP
The standard rule when writing application state information to the registry is to use the HKEY_LOCAL_MACHINE or the HKEY_CURRENT_USER key, depending on whether the setting is global to all users or specific to the current user. Then you should use the Software key. Under this key, you should use your company name, like Borland was used on line 2. Under this key, you should use your product name and then the version number. Under this version number key, you can create values for each piece of state information that you want to store.

Lines 13 to 20 use the queryValue() method to retrieve values from registry keys. These values are stored to the text objects on the form so that the user can view and edit the values. If the user edits the values and submits the form, the saveButton_onServerClick(), beginning on line 59, uses the setValue() method to write the new values back to the registry.

The readRemoteAgents() method begins on line 34. This method creates an array of values that are read from the registry. The remote agent values have names such as REMOTE0 and REMOTE2. There can be several values, which may or may not have sequential numeric values at the end. This method uses the enumValue() method on line 39 to create an array with all the values in the registry key. Lines 41 to 48 then check each value in the array to see whether it begins with the word 'REMOTE'. If it does, the method calls queryValue() and stores the result to a second array, which is then displayed to the user.

The deleteButton_onServerClick() method, which begins on line 66, uses the deleteValue() method to remove items from the registry.

Extended Date Class

A final class worth noting here is the DateEx class that is also found in the Apps\Shared folder. This class is contained in the Dateex.js file. The DateEx class is an extension to the standard Date class, like the StringEx class is an extension to the String class. In addition to the standard properties and methods of the Date class, the DateEx class adds several additional methods:

These methods all return strings representing part of the date value.

NOTE
Both the content and the exact structure of the strings that DateEx methods return are based on the configuration settings of Windows itself. The country settings in the Windows Control Panel determine how date and time information is displayed.

The getSDate() method returns the date as a character string. In the United States, this string would typically appear in the format "Saturday, December 30, 1995". The following example would typically store this string to the variable x:


var d = new DateEx(95,11,30,17,0,0);

x = d.getSDate();

The getSDay() method returns the day of the week, such as "Saturday" or "Tuesday". The actual string returned matches the language version of Windows as well as the country setting.

The getSMonth() method returns the name of the month, such as "December" or "August". Again, the actual names returned are dependent upon the country settings.

The getSTime() method returns the time portion of the DateEx value as a character string. The default formatting of the string depends upon the Windows settings. The optional Boolean <bSeconds> parameter determines whether the return string includes seconds. If you include this parameter, you might also specify the second optional Boolean parameter, <bTwelveHour>. This determines whether the return string uses a 12-hour clock (true) or a 24-hour clock (false).

The getSTimezone() method returns the name of the time zone to which the machine is set. The actual strings are hard coded into the Dateex.js file. Many of the time zone names are debatable and can be changed to match your preference. To change these names, edit the Dateex.js file.

The following script shows two functions taken from the Entries.jrp file in the Apps\Guestbk folder. These two functions make use of the DateEx class to format the date and time in the Guest Book Entries report:


function getVisitDate(frm) {

  var dateEx = new DateEx(""+frm.guest1.rowset.fields["VisitTime"].value);

  return (dateEx.getSDate());

}

function getVisitTime(frm) {

  var dateEx = new DateEx(""+frm.guest1.rowset.fields["VisitTime"].value);

  return (dateEx.getSTime(false,true) + " (" + dateEx.getSTimezone() + ")");

}

The VisitTime field already contains a Date type value. Notice the technique used to create a DateEx object from the existing Date object. The Date object is converted to a string by concatenating it with an empty string (""). The string is a valid parameter when creating a Date or DateEx object.

JavaScript Functions

In addition to the visual components and classes that ship with IntraBuilder, several useful functions are tucked away among the prebuilt business solutions. When these function library files have been loaded, the functions are used like standard functions such as escape() or parseInt().

The String.js file in the Apps\Shared folder contains a handful of string manipulation functions. These are all rather simple; however, they do simplify your own coding and make for more readable code.

There are three functions for trimming strings:

All three of these take a single string parameter, <expS>. The ltrim() function removes spaces on the left side of the string, and rtrim()removes spaces on the right side of the string. The alltrim() function removes both leading (left) and trailing (right) spaces.

There is also a function for padding strings:


pad( <expS>, <expN> )

The pad() function takes a string parameter, <expS>, and a numeric parameter, <expN>. The string is padded with enough spaces to make the length <expN>. The returned string is exactly <expN> characters long. If <expS> has a length that is already greater than <expN>, the returned string is actually shorter than the original.

Another function in String.js converts numeric values to string values:


str( <expN1> [,<expN2> [,<expN3>]] )

The str() function takes at least one parameter, <expN1>, which is the number to be converted to a string. Optionally, a length can be passed, as well as the number of decimal places that should appear in the returned string. If no length is passed, the returned string is as long as necessary to hold the whole number portion of <expN1>. If a length is passed but not a decimal parameter, no decimals are included in the returned string.

Here are several examples of using the str() function. The values stored to x are shown in comments after the function:


x = str(1.23)     // "1"

x = str(1.23,4)   // "   1" (three spaces before 1)

x = str(1.23,4,2) // "1.23"

x = str(1,4,2)    // "1.00"

As you see from the last two examples in the preceding code, the str() function is very useful when trying to display numeric values with a consistent number of decimal places.

The last function in String.js is used to escape characters in a string. To escape a character simply means to identify it as a special character. You identify certain special characters by preceding them with a slash character. For example, the following command normally produces an error:


var x = 'Joe's Fender Shop'

The use of the apostrophe inside the quoted string produces too many apostrophes, and an error results. To correct this, you can escape the apostrophe:


var x = 'Joe\'s Fender Shop'

The escapeChar() function goes through a string and escapes all of a particular character. The syntax for the function is as follows:


escapeChar( <expS1>, <expS2> )

In this syntax, <expS1> is the original string and <expS2> is the character that needs to be escaped. The following example escapes a string before sending it to the rowset's applyLocate() method:


var x = escapeChar( this.form.text1.value, "'" );

this.form.applyLocate("name='" + x + "'");

Application Security Components

The application security components are some of the most powerful reusable components in IntraBuilder. Using these components, you can provide very powerful and very flexible application security. Application-level security allows you to control access by user or by group. You determine what parts of an application each user is given access to. Using this, you can establish security rules governing not only the parts of your application, but also your data, right down to the field level.

The security system is composed of several different parts, including the administrative tools, the security scripting API, reusable security form classes, and the security data files.

These components are used to manage users, groups, resources, and policies. Users represent a single person or process that needs access to secured resources. Groups allow you to identify logical collections of users that have shared access to resources. Resources are logical representations of any application, form, data file, or process to which you want to control access. You assign access to these resources to individual users or to groups. And policies are security rules that apply to all users, groups, and resources. As an example, one of the system policies determines whether or not passwords are case sensitive.

Security Administration Tools

The Security Administration tools enable you to view and update the entities described in the preceding section. These tools are located in the Apps\Security folder. The main form is called SmAdmin.jfm. You can run this form inside the IntraBuilder IDE, or you can run it over the Web.

In order to run this form, you must log into the security system as a user that is part of the ADMINISTRATORS group. The default administrator is named SYSDBA and has the password masterkey. Use this user to initially log into the Security Administrator.

The user name (as well as group, resources, and policy names) are never case sensitive. By default, passwords are not case sensitive either, but you can change this using the Policy Administration form.

The SYSDBA user can be known to anyone who has used the application security system. At your earliest convenience, you should create a new user for yourself and add that user to the ADMINISTRATORS group. Then delete the SYSDBA user or change the password for this user.

After you have logged in, you can view each of the four types of entities (users, groups, resources, and policies). Figure 17.6 shows the Security Administration form running in the IntraBuilder IDE. Notice that you have options to add, copy, update, or delete entities.

Figure 17.6 : The Security Administrator.

The Add option creates a new entity. The Copy option creates a new entity that contains the same attributes as the currently selected entity. The Update option allows you to change the attributes of the currently selected entity. The Delete option removes the currently selected entity.

You are not prompted to confirm deletes. Take extra care around that button.

Entity Administration Forms

The User Administration form allows you to add, copy, or update a user entity. The user name cannot be changed while updating an existing user. The other pieces of information can be modified for both new and existing users. The description is optional. By default, users must have a password with a minimum length of four characters. This minimum length is controlled by a system policy that is described in the next section. As the system administrator, you can disable a user's account by checking the Account disabled check box. The Account locked out check box is used to unlock a user account after the security system has automatically disabled the account because of multiple login failures. The lockout behavior is turned off by default. The next section, "System Policies," describes the lockout feature.

The User Administration form also allows you to assign this user to groups and to grant access to resources. You use the Add Group and Remove Group buttons to move items between the list boxes. The list boxes to the left indicate which groups or resources this user is associated with.

The Group Administration form works very much like the User Administration form. After entering the group name and description, you can associate users with the group and grant the group access to resources. You might want to use many logical groups and then use these groups to control access to resources. This is generally much easier than trying to grant access to many individual users. Pay particular attention to the ADMINISTRATORS group. Any member of this group is able to run the Security Administration application. There is also one special restriction associated with this group. You are not allowed to remove the current user from the ADMINISTRATORS group. An administrator must be removed by a different administrator.

The Resource Administration form is very similar to the Group Administration form. Again, it has a place for the resource name and description and then two sets of list boxes for granting access to groups and users. You create resources that represent any application or part of an application. Using the scripting API described later in this chapter in the "Security Scripting APIs" section, you make sure that only authorized users access these resources.

The final entity administration form is used to configure policies. The Policy Administration form lets you set the policy name and description. Policies can contain either a Boolean or a numeric value. There is a check box for setting the Boolean value and a text component for entering the numeric value. There are also two radio components to determine the data type of the policy value.

The Policy Administration form lets you update system policies as well as create your own. The system policies are policies that control the operation of the security system itself.

NOTE
You can change the description and value of these policies, but you cannot delete them or change their value type. You are free to create, modify, and delete your own policies.

Policies are rules that apply to all users. If you create your own policies, your scripts are responsible for checking these policy settings and responding accordingly.

System Policies

Six system policies have to do with passwords and lockout security. The PASSMIN and PASSMAX policies determine the minimum and maximum length of a password. The default values are 4 and 20, respectively. This is enforced when you create a new user or change the password of an existing user. Setting this policy does not affect existing passwords; however, the policy is enforced the next time the password changes. The CASE policy determines whether or not passwords are case sensitive.

The other three system policies control the lockout security feature. Lockout security disables a user's account if there are several failed login attempts in a short period of time. The LOCOUNT policy sets the number of failed logins that must occur before a lockout. If this is set to zero, lockout security is disabled. The LOMINUTES policy specifies the time frame in which these failed logins must occur. And AUTORESET determines whether the locked out account is reset after LOMINUTES have passed, or whether an administrator must manually unlock the account.

Security Scripting API

The security administration tools let you configure the entities in your security system. You make use of these entities in your application through the security scripting API. This API is implemented as two class libraries. You work exclusively with the SecurityManager class, unless you are writing your own security administration tools. A few notes about the SecurityManagerAdmin class are included at the end of this section.

The SecurityManager class is defined in the Security.js file in the Apps\Shared folder. Before using this class, you must load that script file. Then you can instantiate a SecurityManager object, which represents a single user entity. Although this object contains several methods, you will probably rely on login() and hasAccessTo() the most. The following example creates an object, logs in, and checks to see that the logged-in user can access a particular form:


_sys.scripts.load(_sys.env.home()+"apps\\shared\\security.js");

var user = new SecurityManager();

user.login("erik","erik")

if (user.hasAccessTo("PHONE UPDATE")) {

   // run the form

} else {

   // run an Access Denied form

}

This example actually logs into the system with a fixed user name and password. In your own applications, you should provide a login form where the user enters this information. A reusable login form is provided and is described in the next section.

The preceding example assumes that no errors are generated by the security system. This should never be assumed. The security system reports errors by throwing exceptions, as described in Day 16, "Debugging and Error Handling." Because the constructor and the methods can throw exceptions, surround these calls with a try block and catch the thrown exceptions with a catch block. The exceptions thrown are of a custom exception class SmException. Preprocessor constants are defined for the exception code values in the Security.h header file. This is demonstrated in Listing 17.3.


Listing 17.3. Using the SecurityManager class.

 1: #include "security.h"

 2: _sys.scripts.load(SM_CLASS_LOCATION + "security.js");

 3: try {

 4:    var user = new SecurityManager();

 5:    user.login("erik","erik")

 7:    if (user.hasAccessTo("PHONE UPDATE")) {

 8:       // run the form

 9:    }

10:    else {

11:       // run an Access Denied form

12:    }

13: }

14: catch (SmException e) {

15:    switch (e.code) {

16:       case SM_ERROR_BDE_ALIAS_MISSING:

17:          alert("Security Data Not Available");

18:          break;

19:       case SM_ERROR_LOGIN_LOCKOUT:

20:          alert("Too many invalid login attempts");

21:          break;

22:       default:

23:          alert("Oops, something went wrong");

24:    }

25: }


Line 1 includes the security header file. This file contains the SM_CLASS_LOCATION constant that is used on line 2, when the security classes are loaded into memory.

The try block on lines 3 to 13 contains the actual calls to the security class. The catch block on lines 14 to 25 handles any exception that the security class might throw.

The constructor for the SecurityManager class takes no parameters. It can throw an exception with a code value of SM_ERROR_BDE_ALIAS_MISSING if it cannot find the security data. By default, the data is found using the IBAPPS alias in the BDE. If you move the data to a new folder, you must create a BDE alias for that folder. Then edit the Security.h file. There is currently a line like this:


#define SM_DATABASE_ALIAS "ibapps"

Change this line to contain the new alias name.

SecurityManager Methods

The full list of methods for the SecurityManager class is covered in the following list, along with a brief description of each. The code values of exceptions that can be thrown from the method are also listed:

Administrative Scripting API

In addition to the SecurityManager class, you also have a SecurityManagerAdmin class. This class is also defined in Security.js. It is derived from the SecurityManager class, so it contains all the same methods as SecurityManager. In addition, it contains methods for creating, deleting, and updating the various security entities. There are also methods for relating the entities (adding users to groups, granting access to resources, and so on).

If you use the Security Administration forms to manage the security system, you might never need to use this class. However, if you want to manage the security information on the fly, use this class. For instance, you might allow new users to the site to enter a user name and password. You then use the SecurityManagerAdmin class to add them to the security system.

For more information about the SecurityManagerAdmin class, take a look at the Security.js file itself. In addition, you can look at the Security Administration forms to see how to use this class.

Reusable Form Classes

There are three reusable form classes in the Apps\Shared folder. These classes can be used as the basis of other forms that you create in the Form Designer. The base forms contain all of the necessary logic; all you have to do is apply appropriate user interface elements such as headings or colors, so that the form blends into the rest of your application. The three base form classes contain the logic necessary to perform the following tasks:

The base form classes are each contained in their own .jcf file. You can create a form derived from a base form class by following these steps:

  1. Open a blank form in the Form Designer.
  2. Select File|Set Custom Form Class from the menu.
  3. Press the tool button and select the desired base form from the Apps\Shared folder.
  4. Press the OK button to close the Set Custom Form Class dialog.

This causes the form to redraw with all of the components contained in the base form class. Then change the properties of the form or components, and add your own components so that the form follows the same look and feel as the rest of your application.

Working with Security Base Forms

All three reusable form classes require you to set custom properties before the forms are opened. The three forms take slightly different properties.

The smLoginForm class in Security.jcf requires two custom properties:

The security property must be set to an existing SecurityManager object. The nextForm property is set to an object reference variable for a form that has already been created. The form is opened only if a valid user name and password are entered. It is up to the form itself to check that the valid user actually has access to the resource. Listing 17.4 shows the header section from the Update.jfm file in the Apps\Phone folder. This file uses the smLoginForm class and demonstrates the setting of the two custom properties.


Listing 17.4. Using the smLoginForm class.

 1: var f = new updateForm();

 2: f.argv = UPDATE.arguments;

 3: // then create a security object

 4: _sys.scripts.load(SM_CLASS_LOCATION + "security.js");

 5: f.security = new SecurityManager();

 6: // then create the login form

 7: _sys.scripts.load("emplogin.jfm");

 8: var login = new emploginForm();

 9: // the login form requires two custom properties be set

10: login.security = f.security;

11: login.nextForm = f;

12: // finally, open the login form

13: login.open();

14: return;

15: // {End Header} Do not remove this comment//

16: // Generated on 11/12/96

17: //

18: var f = new updateForm();

19: f.open();


Line 1 creates a form object based on the updateForm class defined in the rest of the file. Normally this object is created and then immediately opened.

Line 4 loads the security classes into memory, and then line 5 creates a SecurityManager object. This object is created as a property of the update form.

Line 7 loads the Phone Book login form into memory. This form was derived from the smLoginForm class. Line 8 creates the form object for the login form.

Lines 10 and 11 assign the custom properties of the login form. The security property is set to the SecurityManager object that was created on line 5, and the nextForm property is set to the Form object created on line 1.

Normally, the header section of a form file opens the form that is defined in that file. In this case, the login form is opened instead (on line 13). The update form itself is opened by the login form after a successful login.

Notice that the header section ends with the return on line 14. This prevents the standard instantiation routine, on lines 18 and 19, from executing.

The smProfileForm class in Profile.jcf has four custom properties:

The security property is assigned an existing security object. The callingForm property is assigned the form reference for the form that called the profile form. When the profile form is closed, focus is returned to the callingForm form.

The profile form contains a button that can be pressed to change the user's password. The name of the change password form and the name of the class defined in that .jfm file are assigned to the changeForm and changeFormClass properties, respectively.

The smChangePasswordForm defined in Change.jcf takes two custom properties:

The security property is assigned an existing security object. The callingForm property is assigned the form reference for the form that called the change password form. When the change password form is closed, focus is returned to the callingForm form.

To see examples of these three forms, you can run the Phone Book Update form. From the main Index.htm page for the prebuilt solution applications, select the link for "Phone List Administration". This opens up a login form based on the class in Security.jcf. When prompted for the user name and password, enter ABRAHAM for both. This opens the record for Abraham. There should be a button at the top labeled Security Profile. Pressing this button opens a form based on the class in Profile.jcf. Pressing the Change PasswordÉ button on that page opens a form based on the class in Change.jcf.

Data Files

The security data is stored in two files in the Apps\Data folder. These tables already contain security data for the prebuilt solution applications. If you want to start from scratch, you can use the copies of the tables in the Apps\Security folder. The two data files are called smentity.db and smassign.db.

Both of these files are password protected. You cannot open these files without knowing the password. The security scripting API uses the password to automatically open the table for its own use, so that the user never needs to have access to this password. This password is hard coded at the top of the application security API file (Apps\Shared\Security.js). The password (masterkey) is available to everyone who opens that file (or reads this sentence), so you might want to customize the password. To change the password in the data files themselves, follow these steps:

  1. Change the working folder in the IntraBuilder Explorer to the folder containing the data files.
  2. Select the File|Database Administration menu choice. This opens the Database Administration dialog shown in Figure 17.7.
    Figure 17.7 : Changing the security table password.

  3. Change the Table Type combo box to PARADOX and select the SecurityÉ button. This opens the Security dialog, which is also shown in Figure 17.7.
  4. Select one of the two tables from the Table list box, and press the Edit TableÉ button. This opens the Password dialog, also shown in Figure 17.7.
  5. Enter the current password, which is masterkey by default.
  6. In the next dialog, you enter and confirm the new master password.
  7. Repeat these steps for the other table.

After you change the password for these two tables, you must also update the Apps\ Shared\Security.js file to match. The following line should appear at the very top of this file:


#define SM_PASSWORD "masterkey"

Simply change the text inside the quotation marks to match the new master password for the table. To ensure the integrity of this password after making this change, you can compile the script and remove the source file from the machine. You can compile the script by right-clicking on the Security.js file and selecting the Compile Script option. Then copy the Security.js file to a secure place and delete it from this machine. Be sure to delete only the Security.js file, leaving behind the Security.jo file. The .JO file must remain.

When you change the password on these tables and remove the source file that contains that password, you have a very secure security system in place. Please note that this security system keeps the data itself pretty secure, but you also need to take steps to ensure that unauthorized users cannot delete or replace the data files. And I qualify the security level as "pretty secure" because the system is as solid as JavaScript allows.

Summary

Today you went behind the scenes to discover the reusable components that exist in the prebuilt business solution applications and the samples that ship with IntraBuilder. These applications and samples provide many visual and non-visual components that you can use in your own applications.

The visual components consist of nearly 50 custom components that you can drop onto your forms. These components provide many different capabilities, reducing the amount of coding that you'll have to do in the long run.

The non-visual components are there to make your programming life just a little easier. The header files, functions, and classes allow you to leverage existing code.

The application security components combine visual and non-visual components to provide you with an application security system that can stand up to the demands of intranets and the Internet.

Q&A

Q:The reusable JavaScript components that come with IntraBuilder all seem to be server-side components. Are there any reusable client-side components?
A:There are many reusable client-side components available on the Web. A list of JavaScript resources can be found on the Borland Web site. At the time of printing, this list is found at
http://www.borland.com/intrabuilder/javasrc.html
Q:Are the sample files the same in version 1.0 and 1.01?
A:No. The sample files for 1.0 were created as the product itself was created. The samples were not able to use some features that came online late in the product cycle. The samples that shipped with the 1.01 release are more mature and feature-rich. The samples discussed in today's lesson are the ones that ship with 1.01.
Q:Where can I get more information about the Windows registry itself?
A:The Microsoft Developer Network provides a wealth of information about the Windows environment. There are also many books about Windows 95 and Windows NT on the market. You can find some great information on the Microsoft Web site; however, it takes a bit of searching. At the time of this writing, general information about the system registry can be found at
http://www.microsoft.com/win32dev/uiguide/uigui255.htm

Workshop

The Workshop section provides questions and exercises to help you get a better feel for the material you learned today. Try to answer the questions and at least think about the exercises before moving on to tomorrow's lesson. You'll find the answers to the questions in Appendix A, "Answers to Quiz Questions."

Quiz

  1. How do you load a custom component file in the Form Designer?
  2. Where do you find the ActiveX.cc custom component file?
  3. What is output when the following script executes:
    #include "intra.h"
    #ifdef DEBUG
    alert("Debugging");
    #else
    alert(OUTPUT_HTML);
    #endif
  4. What four entities are managed by the application security components?

Exercises

  1. Use the expert to generate a form for the orders.dbf table in the Samples folder. Then replace the Text component for the customer number with a FieldSelect component. This component should display a list of customer numbers from the customer.dbf table.
  2. Add a counter component to the form you created in the first exercise.
  3. Write a script to write your name to the following registry key:
    HKEY_CURRENT_USER\Software\Test\
    The value name should be MyName. After writing the value, have the script query the value and display it in an alert dialog. Then delete the value.
  4. Write a function that takes a user name, password, and resource name. It should return a Boolean true if the user has access to the resource and a Boolean false if the user does not. Test with these two sets of parameters:
    alert(check( "Erik", "Erik", "Phone Update" ));
    alert(check( "Test", "Test", "Test" ));
    The first one should return true and the second should return false.