NEW APPLICATION PROCEDURE				01/20/93
-------------------------

Following is a general outline of what steps should be taken to turn the COODBOOK.PJX into your own application.

Note: I have built new applications with this method a couple of times and with a single edit screen and one database it took about three hours from start to finish. When you want fancy reports, or other features, and depending on your expertise, it will take a little longer.

================================================================
DO THE FOLLOWING FIRST

Make a new directory with subdirectories that will hold the trimmed down version of the CD system. This will be your template for all new applications.

\NEWAPP
   \DBFS
   \MENUS    
   \OTHER    
   \QUERIES  
   \REPORTS  
   \SCREENS  

Copy all the \CODEBOOK\CD files to this new directory.

Change the name of the old project file (CODEBOOK.PJX --> NEWAPP.PJX CODEBOOK.PJT --> NEWAPP.PJT) & open it.

Change the Project Options to your specifications.

Note: When you edit some of the programs, you may have to relocate them. Some are common files located in CODEBOOK\COMMON.

Remove all application specific databases, indexes, & memos from project:  CD, CUSTOMER, PURCHASE & maybe STATES;  .DBF/.CDX/.FPT

Delete all application specific databases, indexes, & memos from directory: CD, CUSTOMER, PURCHASE & maybe STATES;  .DBF/.CDX/.FPT

Remove CSZ program (Format city/state/zip) if you are not going to use the STATES database & valid picklist.

Remove old Screens from project:  CD, CUSTOME, GETORDER, MUSICIAN & maybe STATES

Remove old Screens from directory:  CD, CUSTOME, GETORDER, MUSICIAN & maybe STATES;  .SCX/.SCT

Remove the Reports from project:  LISTCD, LISTCUST & ORDERS

Remove the Reports from directory:  LISTCD, LISTCUST & ORDERS;  .FRT/.FRX

Remove Labels from project if desired:  CUSTLBL

Remove Labels from directory if desired:  CUSTLBL; .LBT/.LBX

Remove Query from project if desired:  ORDERS

Remove Query from directory if desired:  ORDERS;  .QPR/.QPX

Remove *.LST files from project. If you want similar files, they probably should be renamed to *.TXT so they can be easily opened from the Open files, FILES type window, and modified with the FoxPro editor. They should also be excluded from the project, since you probably don't want them compiled into your application. These files can be information files: buglist, to-do, changes, etc.

TRAFICOP.PRG
	Change the name of the user in TRAFICOP.PRG if you use such a DOS environment variable (two places):
	IF GETENV("USER") # "XXX"
	IF GETENV("USER") = "XXX"

	If you are not going to use the error trap provided, change the following line to your own error trap:
	ON ERROR DO OnError WITH MESSAGE(), MESSAGE(1), LINENO(), ERROR()

	If you are developing with this project you probably do not want to to quit FoxPro every time you exit the application. You can add the following right before the end of the program in TRAFICOP.PRG:
		IF GETENV("USER") # "XXX"
			QUIT
		ENDIF

		*-- End program

	It's a good idea to create a program (RESET.PRG) that will allow you to reset the environment when you are debugging with new applications. Mine is just set up like this:

		CLEAR WINDOW ALL
		* CLOSE DATABASES
		SET HELP TO D:\FOXPRO2\FOXHELP.DBF 
		SET HELP ON 
		SET RESOURCE TO D:\FOXPRO2\FOXUSER.DBF
		SET RESOURCE ON
		ON ERROR 
		POP MENU _MSYSMENU
		CLEAR

Load and review YAGERR.TXT from Lib 6 of the Foxforum on Compuserve. Make all necessary changes found in this file to the programs and the book.

There are many messages in the FOXFORUM that relate to the CODEBOOK model. You may want to review them to see if you wish to put any of the code or ideas in them into your new application. Some correct potential problems, some may speed up the application a bit, some allow you better control of the application; you have to choose what you want to include based on your own need.

================================================================
DO THE FOLLOWING FOR EACH NEW APPLICATION

Make a new directory with subdirectories that will hold your new application.

\XXXXXXXX
   \DBFS
   \MENUS    
   \OTHER    
   \QUERIES  
   \REPORTS  
   \SCREENS  

Copy all the NEWAPP files to this new directory.

NOTE: If a directory is empty, and you use an application such as XTree to copy it, the empty directory may not be created. You will have to create the directory either from DOS of from the application if you intend to put anything in it.

Change name of the old project file (NEWAPP.PJX --> YOURAPP.PJX NEWAPP.PJT --> YOURAPP.PJT) & open it.

-----------------------------------------------------------------
DATABASES

Create new databases and indexes in new directory.
	NOTE: If you are going to be using large databases, especially on a network, you need to optimize them for RUSHMORE technology. Because the MCONTROL screen set counts records every time you move from one record to any other, and because DELETED is usually set ON, one of the things you absolutely have to do is create an index on deleted records. As you are creating new or modifying your old indexes simply create another one in the Expression Builder with the expression DELETED() with the tag name DelRec.

Add the new databases to the project & Exlcude them. If you don't Exclude them they will be compiled into your new application. A very large database will make a very large application.

DATADICT.DBF:
	Modify DATADICT.DBF to reflect the new databases or use my program MAKEDICT.APP (see below) to create a new DATADICT.DBF, and then add the needed information. 

	You have to change all the Lindex fields to .F. that you don't want indexed and you must put in the Ctag field description before you can order the database from the application. If you change any information in this database you have to rebuild the application to reflect the changes since this database is not excluded.

	Also if you added DELETED() records to your database make sure this is in the DATADICT.DBF as well. 

	NOTE: Because this is normally not an excluded database, make sure that you remove the reference to the old index in the project and then add the new index.

	See Chapter 10, page 111 for information about the DATADICT.DBF.

	To use MAKEDICT.APP (MAKDIC.ZIP is in library 2 on Compuserve) copy it and the \DBF\DATADICT.* files to the same directory as your databases. Open & ZAP the DATADICT.DBF then close it. DO MAKEDICT to choose your databases & create the new DATADICT.DBF & then move the three DATADICT.* files back to \DBF directory.

SETUP.DBF:
	Change the information in SETUP.DBF. (Some of the fields in this database will be used for display on BCKGRND screen). 

	If you are using AUTOID to automatically increment the ID number of a database field, change Cdid to the alias of the database and make it up to 7 characters long + 'id', i.e. if the alias of the database is ABCDEFGH, then change the field name 'Cdid' to 'Abcdefgid'. You can also change or delete 'Customeid' or add more fields this way. If you are using this feature, make sure the field names and types in the AUTOID.PRG match your database; the CODEBOOK.APP & AUTOID.PRG use character fields for the ID field. You will have to modify AUTOID.PRG if you use numeric fields for your ID fields.

	If you are using AUTOID.PRG on a network you probably want to lock out the record while you are getting the new ID, incrementing it, and writing it back. This will make sure that two or more users don't get get the same number. There are a number of ways of doing this using the RLOCK() function. See the FoxPro manual for its use.

CDRSRC.DBF:
	Rename CDRSRC.DBF resource file to xxxxRSRC.DBF; ZAP if desired.

	Remove CDRSRC.DBF from project & add xxxxRSRC.DBF.
	
	NOTE:
	If you want the user to be able to enter notes in the CALENDAR/DIARY, or you are going to be debugging and need to change the positions of the TRACE & DEBUG screens, or want to change what is listed in the browse screen, or want to change the colors, exclude this file so you can write to it and move it from the \XXXXXXXX\DBFS directory to the \XXXXXXXX directory. If you don't want the user to modify anything in this file remove the exclude and put it back into the \XXXXXXXX\DBFS directory.

CODEHELP.DBF:
	Remove CODEHELP.DBF from project, change its name to XXXXHELP, and add XXXXHELP to project; .DBF/.FPT. Modify any help records required. The records to change: About CD Cataloguer..., Listings..., CD Catalog, Customer Info/Order, CD Entry Window, Artist Entry Window, Customer Information Window. If you change any of the help records, you should try to make them as generic as possible so you don't have to re-edit them every time you make a new application. For instance, Listings... can be made generic without having to mention the database names. You should also delete any records you don't want and pack this file when you are finished modifying it.

SECURITY.DBF:
	If removing SECURITY.DBF make sure that the SEEK in the MAIN.MNX menu Program Options is removed as well. It might be better just to leave this database in your application since it not very large and it could be useful in the future. By using security you can restrict users from some of the menu options.

	Because this is normally not an excluded database, and if you are using  and changing it, make sure that you remove the reference to the old index in the project and then add the new index.

	See HOW SECURITY WORKS later.

CALLWHOM.DBF
	If you are using PRO2EROR.DBF for an error trap, add this database to your project. You need not exclude it since no one is modifying it and it contains your information only.
	
-----------------------------------------------------------------
NOTE: After you have written your new application, and you have not excluded some of your databases, and then move the databases to another drive and/or directory, when you run your application you may get an error that you can't open the file. This is because the non-excluded databases point to where you originally created them. If you tailor the help file and resource file you probably want to exclude them.

-----------------------------------------------------------------
MENUS

Modify MAIN.MNX:
The called programs and menu items under Prompt \<Program have to be changed. If you are going to use Security, check: 'Options', 'Skip For' and add SEEK("ModuXXXXXX C","Security").

Setup Section:
	The CODEHELP & CODERSRC have to be changed to your new names: XXXXHELP & XXXXRSRC.
	If using security the user ID should be changed to get the user's ID:
		PUBLIC gcUserID
		m.gcUserID = GETENV("USER")
		See page 41 for information on how to use security.

Cleanup Section:
	Modify '*-- Procedure OpenDbf' to open new databases & set order.

If using a picklist, change array name the database will go into.

Set filter if required. 

In some sections of the Menu such as the Submenu of the Menu Window Pad,
     \<Window         Submenu   <  Edit   >     [X]  
                                       |
                                       v
        \<Move           Bar #     _MWI_MOVE        [X] 
   
you can see _MWI_MOVE. This is a System Menu Name that is used by FoxPro 2 and that can also be used by you in your menus. Look in the "Commands & Functions" manual under 'MENU - System Menu Names' to see a list of these names; also see the "Developer's Guide" chapter on Menus, 'Using FoxPro System Options' . These can easily be added to the menu if you want to add, say, Macros to your application, or the Debug window, which is handy, if you are debugging your application.

If you are going to run your application on a system with monographics or other displays that can't handle extended video modes, you need to make sure that the menu option in the System/Environment/Extended Video 'Options' is checked and the 'Skip For...' is checked. Here you would put the code to check for any displays you don't want. You do this with the SYS(2006) command. If you don't, and the user on a mono system chooses this menu option, you are sure to bomb the application.

-----------------------------------------------------------------
SCREENS

Create new screens for your application, or copy the ones from CODEBOOK,  rename them, and then modify them (see below). Screen Code Options should have checked: Define Window & READ CYCLE.

When adding a new screen to your project, make sure that each new screen has the MCONTROL screen added to it (MCONTROL is found in \CODEBOOK\COMMON\SCREENS).

NOTE: If using an old database, and you are using memory variables, make sure that the memory variable field names in the screen are the same as in the database. Griver uses the type as the first letter in his field names, and is thus reflected in his variable naming conventions. Your field names may not use these conventions.

Add your new screens to project.
	Add the MCONTROL screen to each of your new screens, and Arrange them.
	Modify new screen:
		Screen name should be 'wrXXXXXX'.
			(Note, the last character, if a number, is the window number. If you want a number in your window name, other than the window number, it should not be placed in the last position.
		Add title if desired.
		Copy the Setup Code, Cleanup Code, & Deactivate from the CD screen set.
		Change the 'CD' in all these sections to your name 'XXXXXX':
			Change the name of the screen window in the screen Setup code:
				*-- Add the proper BAR to the Window POPUP
			Change the name of the screen window in the screen Cleanup code
				in the 'IF glQuitting' section.
		Name your GET fields m.xxxxxx where xxxxxx is your field name; add the field type if desired if your database fields are that way: m.cxxxxxx, m.nxxxxxx, m.lxxxxxx, etc. Note: if you use Quick Screen to pull in your database fields, you can check Memory Variables to name them all automatically.
		For each field add a MESSAGE if desire.
		For each field check DISABLED.

Make sure the Code Options checkboxes for your screens are Define Windows & READ CYCLE only.

If using a Picklist to choose from a database with an object on the screen, create the screen such as the Artist Entry Window on page 233, and the lhMusician push button on page 191.  (Code Options Checked: Define Windows, Release Windows, READ CYCLE, Modal)

If using a Picklist to verify an entry in a GET field, such as the m.cstate GET FIELD in CUSTOMER.SCX, create the list screen such as the State Verification screen on page 173, generate the screen as a xxxxxxxx.PRG file, and add it to the project. (Code Options Checked: Define Windows, Release Windows, READ CYCLE, Modal). Put the seek code in the Valid clause of the field that is to be checked; see the m.cstate get field object on page 216.

How the State verify entry works:  
	If the two letter state abbreviation being entered in the m.cstate GET field cannot be found in the STATES.DBF database [ IF !SEEK(m.cState,"States") ], the STATES.SCX screen is called as a function [ lnTemp = States(ROW(),COL()), because it is compiled as a .PRG file ], and a picklist will appear at the y,x coordinates (the cursor's ROW(),COL() coordinates) of the m.cState GET field that the user can choose from. After selecting the desired state, the pointer (which is the number of the state in the list) will then be used to get the state from the array that was created in the MAIN.SCX screen Cleanup code [ m.cState = gaStates(lnTemp,1) ].

	This method of picking from a list is good if the list is fixed. If you have to add entries to a list database in the same session, you have to redimension and recreate the array from the database again. It seems an alternative is to get the needed information directly from the database if it is found.

If using MOVER.SCX screen the program uses two dimensional arrays. So be sure that they are dimensioned correctly:
	dimension laxxxList(lnCnt,2)
	dimension laxxxPick(1,2)

If not using the auto-increment-ID feature in a screen, and you are using the same Cleanup code from the CD screen, comment out the following line in the 'PROCEDURE Addit' in the Cleanup part of the screen:
	* =AutoID()					&& Increment the ID
If you are using this feature, make sure the field names and variables in the AUTOID.PRG match your database; the CODEBOOK.APP & AUTOID.PRG use character fields for the ID field. You will have to modify AUTOID.PRG if you use numeric fields for your ID fields. See the SETUP.DBF info in the DATABASES section above.

If a field is desired that in effect is a SAY field, where you just want to display a value, make it a GET field with all the same attributes as any other GET field in the screen, and in the WHEN clause of that field put 'SHOW GET m.xxxxxx DISABLE'. When the READ enters this field, the field is immediately exited and you can't edit it.

-----------------------------------------------------------------

Modify BCKGRND screen if necessary. The information in the SETUP.DBF will be used for this screen.

NOTE: If you are going to use this application with users who are not familiar with such a menuing system, it is probably a good idea to put some instructions on the screen, such as how to get to the menu options with a keyboard or mouse, where Help is located, where Quit is located, where the databases are located, and who & where to call for assistance. I.e. I use the following for my screens:

    Keyboard use: Press <Alt> to access top menu,        
                  then highlighted letter.               
       Mouse use: Click on top menu option.              
       Databases: Database access is under Program.      
         To Quit: Quit is under File.                    
                                                         
    For help: Help is under System, or press <F1> key.   
    
The last line I put one line up from the bottom of the screen so that it is visible right under the MCONTROL screen when a program is chosen.

-----------------------------------------------------------------
REPORTS

Create & add your own application specific reports to project.

Modify RepoList.dbf with new report names. See page 177.
	NOTE: Because this is normally not an excluded database, make sure that you remove the reference to the old index in the project and then add the new index.

The RepoList.dbf gets used like this:

	MAIN.MNX (Main menu)
		OUTPUT <Edit> -> Menu -> OUTPUT -> Options...
			Do Reports.SPR     --- REPORTS generated screen program
				REPORTS.SCX     --- REPORTS screen
					RepoList.dbf --- in REPORTS SETUP
					    |
					    v
					LaReps[]     --- records get put into this array,
					    |        --- the choice from array is put 
					    v        --- into variable lcSeleRepo.
					lcSeleRepo   --- this variable is in REPORTS m.lhok VALID
					    |
					    v
	REPORT FORM lcSeleRepo   --- This line runs the report.

NOTE ON REPORTS:  Reports call Queries so the Queries have to be created before you can generate a report. The Query output should go to CURSOR, and the report form name that the output will go to should be the same as the query name. The CTYPE and CWINDSHOW fields in the REPOLIST.DBF should have 'Repo' in them, and the CSELECT field should be left blank. Generally Reports will have the queries specify what records will go in the report.

NOTE ON LISTS:  Lists do not call Queries but the CTYPE and CWINDSHOW fields in the REPOLIST.DBF should have 'List' in them and the database name has to be put into the CSELECT field. Generally Lists will be complete listings of a database.

Create & add your own application specific labels to project.

Create & add your own application specific Query to project.

----------------------

In REPORTS.SCX there is a snippet 'm.lhok VALID', which is a nice routine to produce a report that can be viewed with the page-up and page-down keys. I needed this type of routine in one of my old programs, so I imported it. It produces a unique file name for the output. I mistakenly replaced 'SYS(2015)' with "HISTORY" and used it on a network with the result that two people were trying to produce a report with the same name, and we got a network error. If you intend to use this routine on a network do not change the following line:
    lcFileName = SYS(2015) + ".REP"
You can also use 'SYS(3)' in place of 'SYS(2015)'.

Note on SYS(2015) and SYS(3):  SYS(2015) produces a 10 character string, for example: _Q9Y0HPINE. If you generate another string shortly after you might get _Q9Y0HPOWV. Notice that only the last few characters are unique. The above line should actually be
	lcFileName = RIGHT(SYS(2015),8) + ".REP"
for a truly unique filename. SYS(3) produces an eight character string so
	lcFileName = SYS(3) + ".REP" would be perfectly all right.

----------------------

There are a couple of changes that have to be made in the REPORTS.SCX as reported in the YAGERR.TXT file mentioned above. Make sure these changes have been made.

----------------------

If you are going to use the thermometer program (THERM.PRG) with reports that are 'not' going to print 'all' the records, that is, with a scope, you have to initialize the variable lcScope since THERM.PRG uses it to count the records. You have to modify the REPORTS.SCX to accomodate the use of this variable.

This is how I modified the REPORTS.SCX lhok VALID:

	IF laReps[m.lnReport,5] = "Repo"      && Run Query for OK or Modify
-->	lcScope = ""
		.
		.
	ELSE 
-->	lcScope = "ALL"
      .
      .
	ENDIF

	CASE m.lcOutPut = "To Screen"
      lcFileName = SYS(2015) + ".REP"

      &lcCommand FORM &lcSeleRepo ;
         TO FILE (lcFileName) ;
-->      &lcScope ENVIRONMENT NOCONSOLE
		.
		.
		.
	CASE m.lcOutPut = "To Print"
		ON ESCAPE DO PRNTRAP
		IF PRINTSTATUS() 
			&lcCommand FORM &lcSeleRepo ;
         	TO PRINTER ;
-->       	&lcScope ENVIRONMENT NOCONSOLE 
			CLEAR READ

	Then you can pass the actual scope to other reports you may have:

	CASE m.lcOutPut = "To Screen"
   	lcFileName = SYS(2015) + ".REP"
-->  	lcScope = "FOR UPPER(arinvt02.descrip) = mdescrip"

    	REPORT FORM INVENQ.FRX ;
      	TO FILE (lcFileName) ;
-->     	&lcScope NOCONSOLE 

----------------------
Here's another trick you can do with reports.

Reports in this application are actually queries that use reports for their output. Some of my queries require one or two variables to be set before they are called, i.e. a set of Fiscal Year dates or the name of a particular Vendor or some other variable. If you need to enter these values before the query executes you can do this by calling a program or screen that asks for the data right before the query executes. You do this by adding another field to the REPOLIST.DBF:
	cProgName -- Character type, 12 characters long.

Change the REPORTS.SCX Setup code that initializes the laReps array and add the new field name:

	SELECT RepoList.cDosName, "  " + Repolist.cFullName, ;
			RepoList.lEditable, RepoList.cSelect, RepoList.cType, ;
-->		RepoList.cProgName ;
		FROM RepoList ;
		WHERE RepoList.cWindShow = lcRepoType ;
		INTO ARRAY laReps

Add the following lines to the REPORTS.SCX m.lhok VALID:

	IF laReps[m.lnReport,5] = "Repo"   && Run Query for OK or Modify
-->	* Check to see if there is something to run before report
-->	IF !EMPTY(laReps[m.lnReport,6])
-->		m.lcProgName = ALLTRIM(laReps[m.lnReport,6])
-->		DO (m.lcProgName)
-->	ENDIF
		lcScope = ""
		SET TALK WINDOW
		SET TALK ON
	   DO ALLTRIM(laReps[m.lnReport,1])+".QPR"
	   SET TALK NOWINDOW
	   SET TALK OFF

Put the program or screen name to run into the cProgName field in the REPOLIST.DBF for the Report that is to run:

CdosnameCfullname     CtypeLeditableCselect CwindshowCprogname  
ͳ
VENDSUM Vendor Totals Repo F                Repo     GETFISC.SPR

Now whenever this Report/Query is requested, the program will run just before the query is executed to get the variables you need.

Before you create your query with RQBE you should have the variables you are using already in memory else you will get an error, both in the query and in the report it uses if your report uses the same variables. For instance, if you are entering in the start and end dates for a fiscal year, and you want to run the query while you are testing it, you want to have something like m.start = CTOD('10/01/89') and m.end = CTOD('09/30/90') which can be entered in the COMMAND window. In the RQBE screen you put them in the Example box:

Field Name            NOT           Example              UpL

RADSERV.DATE_SERV     [ ] Between   M.START,M.END         [ ]

This is a little annoying, and if it gets too frustrating you can just run a program or macro that initializes them.

-----------------------------------------------------------------
OTHER OPTIONS

I use two programs in most of my applications that have to do with the ON ESCAPE command. One is TRAP.PRG and the other is PRNTRAP.PRG. These allow the user to press the <Esc> key and gracefully exit whatever the user is doing. It's suggested to use such a program in your applications. They are simple to write and will save the users from finding themselves in the COMMAND window and not knowing what to do.

I have modified TRAFICOP.PRG program to call this program: 
SET ESCAPE ON
ON ESCAPE DO TRAP

I have also modified REPORTS.SCX:
	CASE m.lcOutPut = "To Print"
-->	ON ESCAPE DO PRNTRAP
		IF PRINTSTATUS() 
			&lcCommand FORM &lcSeleRepo ;
         	TO PRINTER ENVIRONMENT NOCONSOLE 
			CLEAR READ
		ELSE
			DO alert.spr WITH "Printer not ready"
		ENDIF
-->	ON ESCAPE DO TRAP
	ENDCASE

As of this file I haven't checked all the parts of the application that could bomb because of these changes, so stay tuned for further reports.

----------------------

There may be many times when you may want to search on specific fields when a particular database is open. In the CODEBOOK application you can only search when you have the browse/list window open, and only on those fields that are indexed. You can, however, set up a record search menu option by doing the following:

1. Copy the APPSRCH.SCX and APPSRCH.SCT files from the \FOXPRO2\GOODIES\FOXAPP\SCREENS directory to your \XXXXXXXX\SCREENS or \COMMON directory.

2. Add APPSRCH to your project.

3. Edit it and add the following lines to the 'oksrch VALID' snippet:

	These lines are used in case your index in set to upper case.
   CASE TYPE("&fldname") $ "CM"     && character or memo field
      IF m.tagnum > 0
-->     	m.key = KEY(m.tagnum)
-->     	IF left(m.key,5) = 'UPPER'
-->         SEEK UPPER(ALLTRIM(m.srchterm))
-->      ELSE
-->         SEEK ALLTRIM(m.srchterm)
-->      ENDIF
         IF !FOUND()
...

	This line is so that you can see the record when found.
      SET CURSOR OFF
      WAIT WINDOW "Found it!" NOWAIT
-->  	SCATTER MEMVAR MEMO
      CLEAR READ
      
4. Close the screen and open the MAIN menu.

5. Under RECORD and after Delete add Sea\<rch and add the PROC with the following lines:
		Do appsrch.spr
		SHOW GETS
and close the menu.

After rebuilding and running the application, when you open a database the RECORD menu option will become enabled so that Search will be an option. Clicking on this will open the APPSRCH window so that you can search on any field. When found the record will be displayed.

----------------------
SETSETS

If you have users with different types of displays, you might want to add the following to your SETSETS.PRG file. You have to make sure that the color sets are available in the xxxxRSRC file.

m.scrnmode = SYS(2006)
DO CASE
	CASE m.scrnmode = 'VGA/Color'
		SET COLOR SET TO COLOR
	CASE m.scrnmode = 'VGA/Mono'
		SET COLOR SET TO MONOCHROME
	CASE m.scrnmode = 'MDA/Mono'
		SET COLOR SET TO MONO
	OTHERWISE
		SET COLOR SET TO DEFAULT
ENDCASE

-----------------------------------------------------------------
FINAL SETUP

When installing the application onto a network or into its own directory for use or distribution the following files will be needed:

The main application -- XXXXXXXX.APP

The main databases -- XXXXXXXX.DBF
                      XXXXXXXX.CDX
                      XXXXXXXX.FPT
                      
The resource file -- XXXXRSRC.DBF   (if it hasn't been compiled
                     XXXXRSRC.FPT    into the application)

The error database -- ONERROR.DBF
                      ONERROR.FPT   or
                      PRO2EROR.DBF
                      PRO2EROR.FPT
                      
The setup database -- SETUP.DBF

Any macro file -- DEFAULT.FKY    (If macros are available in your menu)

Any reset program -- RESET.FXP

=================================================================
HOW SECURITY WORKS

SECURTIY.DBF has 2 fields     CUSERID             CMENUID
                                                    
                            5 characters       8 characters
See DEFINE PAD              WORKSTATION    4 char     4 char 
    SKIP FOR                   NAME            Menu        Submenu
    in manual                                  bar           
                           Ex: ANNEX                         
                                                Prog    Ex: INVE

----------------
In MENU   MAIN   Setup
	m.gcUserID=GETENV("USER")		USER is a DOS environment variable
	                              that is usually initialized via
	                              AUTOEXEC.BAT:  SET USER=ANNEX
	
In MENU   MAIN   Cleanup
	Open SECURITY.DBF
	SET FILTER TO cUSERID=LEFT(m.gcUserID,5)
	Provides access to only those records in SECURITY that match the USER.

----------------
In MENU   Prompt   Options   [X] Skip For... is checked
The Expression Builder contains:  SEEK("ProgINVE","Security")

----------------
So only the records in SECURITY.DBF ANNEX are available, and when a SEEK is done, if "ProgINVE" is found a TRUE condition occurs and the menu entry is Skipped.

Thus, if you want a user to _NOT_ have access to a menu item, you have to put that user and menu item in the SECURITY database.

----------------
When you have a lot of users this is cumbersome. It is better to have a database that contains users and what they have access to.

CuseridCmenuid 

ANNEX  ProgINVE This user can access five menu options.
ANNEX  ProgPROD 
ANNEX  ProgSUBP 
ANNEX  ProgCREA 
ANNEX  ProgGENE 
TRAFF  ProgCREA This user can only access two menu options.
TRAFF  ProgGENE 
SHOP3  ProgINVE This user can only access three menu options.
SHOP3  ProgPROD 
SHOP3  ProgSUBP 

This means you have to change MAIN.MNX in the following ways:

In MENU   MAIN   Setup
* Set user ID for SECURITY
PUBLIC gcUserID
m.gcUserID = GETENV("USER")
m.gcUserID = PADR(m.gcUserID,5)  <--  Makes sure names < 5 charactures
                                      in length have spaces in them.

In MENU   MAIN   Cleanup
IF USED("Security")
	SELECT Security
	SET ORDER TO TAG cMenuID
ELSE
	SELECT 0
	USE (LOCFILE("Security.dbf","DBF","Where is Security?"));
		AGAIN ALIAS Security ;
		ORDER TAG cMenuID
ENDIF
SET FILTER TO cUSERID = LEFT(m.gcUserID,5)  <--  In case user name is
                                                 greater than 5 char.

In MAIN.MNX

    Prompt                  Result            Options 
Ŀ 
  \<Program        Submenu   <  Edit   >     [X]  
                                    
                                    v
    Prompt                  Result            Options 
Ŀ 
  Inventory \<Mai  Command   DO Mhit in MAIN  [X] 
                                                  
                                                 v
                                           [X] Skip For...
                                                  
                                                 v
The Expression Builder contains:   ! SEEK("ProgINVE","Security") 
Notice the negation .NOT. (!) in the expression. So if the user is not in the database, the menu item is skipped.

=================================================================
ENCRYPTION

You will notice that there are some databases that are not excluded, such as DATADICT, HELP, REPOLIST, and SECURITY. I purchased a third party program that was encrypted. When you add such a program to your project, your entire application becomes encrypted when you build it. This causes a problem when you try to use the non-excluded databases: they can't be used and you get strange errors. You have to exclued these databases to properly use them in this situation. This is normally not a problem except when you want to use SECURITY: this database is no longer secure if it is excluded. 

The only solution, until this problem gets fixed by Microsoft, is to try and get the vendor to not encrypt the program, or develop some other security method if this is an issue for you.

=================================================================
