You are on page 1of 18

Bonus Chapter Upsizing VFE Security Tables

In Chapter 19 we recommended that you not upsize the VFE security. However, it is hard to convince anyone that a client/server application is secure if the security information is readily accessible in DBF files on the file server which the users must be given full rights to.

The Visual Fox Express security system is very robust and configurable. As a matter of fact, we generally do not define any security beyond setting up the security names and giving our users access to the security screen. In this chapter I will give you the tools and information you need to move the tables to the server. The main caveat here is that the following has been done on SQL Server 7.0. If you are using another version or manufacturer youre on your own, however, we suspect the process will be very similar.

Tables

The first step is to move the security tables to your SQL database. There are several ways that you can do this. The first would be to use the upsizing wizard. This will get you started but not 100% of the way there. The VFE tables have several rules on them which the wizard doesnt handle very well. The second method you could use would be to script them by hand. This method will work well, if you like writing scripts. You could also use enterprise manager to create the tables. However, you are in luck, since I already created these tables for you. I generated a SQL script for you to create them. To run this script, copy it to the SQL Query Analyzer and run it. Be sure you have selected your applications database prior to running the script. The tables will be created with duplicates of all the DBC rules that VFE imposes on these tables.
ALTER TABLE [dbo].[apprites] DROP CONSTRAINT FK_apprites_cgroupd_id GO ALTER TABLE [dbo].[appusers] DROP CONSTRAINT FK_appusers_cgroupd_id GO /****** Object: Table [dbo].[appgroups] Script Date: 1/4/2001 11:17:00 AM ******/ if exists (select * from sysobjects where id = object_id(N'[dbo].[appgroups]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[appgroups] GO /****** Object: Table [dbo].[appinfo] Script Date: 1/4/2001 11:17:00 AM ******/ if exists (select * from sysobjects where id = object_id(N'[dbo].[appinfo]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[appinfo] GO /****** Object: Table [dbo].[applogin] Script Date: 1/4/2001 11:17:00 AM ******/ if exists (select * from sysobjects where id = object_id(N'[dbo].[applogin]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)

drop table [dbo].[applogin] GO /****** Object: Table [dbo].[appnames] Script Date: 1/4/2001 11:17:00 AM ******/ if exists (select * from sysobjects where id = object_id(N'[dbo].[appnames]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[appnames] GO /****** Object: Table [dbo].[apprites] Script Date: 1/4/2001 11:17:00 AM ******/ if exists (select * from sysobjects where id = object_id(N'[dbo].[apprites]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[apprites] GO /****** Object: Table [dbo].[appusers] Script Date: 1/4/2001 11:17:00 AM ******/ if exists (select * from sysobjects where id = object_id(N'[dbo].[appusers]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[appusers] GO /****** Object: Table [dbo].[appgroups] ******/ CREATE TABLE [dbo].[appgroups] ( [cgroup_id] [char] (16) NOT NULL , [cname] [char] (40) NOT NULL , [mdescript] [text] NOT NULL , [timestamp_column] [timestamp] NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO Script Date: 1/4/2001 11:17:06 AM

/****** Object: Table [dbo].[appinfo] Script Date: 1/4/2001 11:17:06 AM ******/ CREATE TABLE [dbo].[appinfo] ( [cid] [char] (16) NOT NULL , [nminpasswordlength] [numeric](2, 0) NOT NULL , [nminuseridlength] [numeric](2, 0) NOT NULL , [nmaxlogins] [numeric](2, 0) NOT NULL , [nfailures] [numeric](2, 0) NOT NULL , [luniquepassword] [bit] NOT NULL , [nchangepassword] [numeric](4, 0) NOT NULL , [ndefaultlevel] [numeric](1, 0) NOT NULL , [timestamp_column] [timestamp] NULL ) ON [PRIMARY] GO /****** Object: Table [dbo].[applogin] ******/ CREATE TABLE [dbo].[applogin] ( [cuserid] [char] (30) NOT NULL , [tloggedin] [datetime] NOT NULL , [tloggedout] [datetime] NULL , [tcleared] [datetime] NULL , [cclearedby] [char] (30) NOT NULL , [timestamp_column] [timestamp] NULL ) ON [PRIMARY] GO Script Date: 1/4/2001 11:17:07 AM

/****** Object: Table [dbo].[appnames] ******/ CREATE TABLE [dbo].[appnames] ( [cname] [char] (40) NOT NULL , [mdescription] [text] NOT NULL , [timestamp_column] [timestamp] NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO /****** Object: Table [dbo].[apprites] ******/ CREATE TABLE [dbo].[apprites] ( [cgroup_id] [char] (16) NOT NULL , [cname] [char] (40) NOT NULL , [nrights] [numeric](1, 0) NOT NULL , [timestamp_column] [timestamp] NULL ) ON [PRIMARY] GO /****** Object: Table [dbo].[appusers] ******/ CREATE TABLE [dbo].[appusers] ( [cuserid] [char] (30) NOT NULL , [cfirst_name] [char] (20) NULL , [clast_name] [char] (25) NULL , [cpassword] [char] (30) NOT NULL , [dlastchanged] [datetime] NOT NULL , [ladministrator] [bit] NOT NULL , [cgroup_id] [char] (16) NULL , [moldpasswords] [text] NOT NULL , [linactive] [bit] NOT NULL , [timestamp_column] [timestamp] NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO

Script Date: 1/4/2001 11:17:07 AM

Script Date: 1/4/2001 11:17:07 AM

Script Date: 1/4/2001 11:17:08 AM

ALTER TABLE [dbo].[appgroups] WITH NOCHECK ADD PRIMARY KEY CLUSTERED ( [cgroup_id] ) ON [PRIMARY] GO ALTER TABLE [dbo].[appinfo] WITH NOCHECK ADD PRIMARY KEY CLUSTERED ( [cid] ) ON [PRIMARY] GO ALTER TABLE [dbo].[appnames] WITH NOCHECK ADD PRIMARY KEY CLUSTERED ( [cname] ) ON [PRIMARY] GO ALTER TABLE [dbo].[apprites] WITH NOCHECK ADD CONSTRAINT [PK_apprites] PRIMARY KEY CLUSTERED ( [cgroup_id],

) GO

[cname] ON [PRIMARY]

ALTER TABLE [dbo].[appusers] WITH NOCHECK ADD CONSTRAINT [PK__appusers__2DF4EC6C] PRIMARY KEY ( [cuserid] ) ON [PRIMARY] GO

CLUSTERED

ALTER TABLE [dbo].[appinfo] WITH NOCHECK ADD CHECK ([nchangepassword] >= 0), CHECK ([ndefaultlevel] >= 1 and [ndefaultlevel] <= 3), CHECK ([nfailures] >= 3), CHECK ([nmaxlogins] >= 0), CHECK ([nminpasswordlength] >= 0), CHECK ([nminuseridlength] >= 0) GO ALTER TABLE [dbo].[applogin] WITH NOCHECK ADD CONSTRAINT [DF__applogin__tlogge__255FA66B] DEFAULT (getdate()) FOR [tloggedin], CONSTRAINT [DF__applogin__cclear__2653CAA4] DEFAULT ('') FOR [cclearedby] GO ALTER TABLE [dbo].[appnames] WITH NOCHECK ADD CONSTRAINT [DF__appnames__mdescr__2930374F] DEFAULT ('') FOR [mdescription] GO ALTER TABLE [dbo].[apprites] WITH NOCHECK ADD CONSTRAINT [DF__apprites__nright__2C0CA3FA] DEFAULT (0) FOR [nrights] GO ALTER TABLE [dbo].[appusers] WITH NOCHECK ADD CONSTRAINT [DF__appusers__cfirst__2EE910A5] DEFAULT CONSTRAINT [DF__appusers__clast___2FDD34DE] DEFAULT CONSTRAINT [DF_appusers_cpassword] DEFAULT ('') FOR CONSTRAINT [DF__appusers__dlastc__30D15917] DEFAULT [dlastchanged], CONSTRAINT [DF__appusers__ladmin__31C57D50] DEFAULT CONSTRAINT [DF_appusers_moldpasswords] DEFAULT ('') CONSTRAINT [DF__appusers__linact__32B9A189] DEFAULT GO ALTER TABLE [dbo].[apprites] ADD CONSTRAINT [FK_apprites_cgroupd_id] FOREIGN KEY ( [cgroup_id] ) REFERENCES [dbo].[appgroups] ( [cgroup_id] ) GO ALTER TABLE [dbo].[appusers] ADD CONSTRAINT [FK_appusers_cgroupd_id] FOREIGN KEY ( [cgroup_id] ) REFERENCES [dbo].[appgroups] ( [cgroup_id] ) ('') FOR [cfirst_name], ('') FOR [clast_name], [cpassword], (getdate()) FOR (0) FOR [ladministrator], FOR [moldpasswords], (0) FOR [linactive]

GO

When you run the above script you should get the message Execution Completed without error or similar in your results pane. If you browse your database in enterprise manager you will be able to verify that the tables have been created.

Remote Views

The second step is to create remote views to these tables in your FESYS database. The FESYS database is the database container file that VFE creates to stored the security tables and views. They are created in a separate database so that you can share your security tables with several applications if you wish, or also several data sets for a specific application. The first step to doing this is to create a connection for the remote views. As you know, a connection is the information VFP will use to connect to your SQL Server database. Once this connection is established it is utilized to send the remote view queries to the server. As we showed in the tips chapter you want to share the connection. However, your current connection was created in your applications .DBC file, and not in the FESYS file. We have verified that if you create a connection with the same name that connection will be shared between views in the separate databases. Once you have your connection created you will need to create the remote views which will be used by VFE to retrieve and update data. The views in FESYS are designed to use the LV_/RV_ prefix to views. If you are not familiar with this, what happens is that, based on a property in the application object, lUseLocalData the cursor class knows whether to open the LV_xxx view or the RV_xxx view. In both cases the view will be opened with an alias of V_xxx. What this means is that the first step creating the remote views is to set lUseLocalData to true in your application object. OK, at this point you have created your connection and set you lUseLocalData propert to false. Once again, I will make this easy for you. The following code will create the remote views for you. There are three things you need to do to run this: 1. Change the #DEFINE of the first line of code to the name of the connection that you have created in place of NameOfYourConnection. 2. At the command window open the FESYS database using the command OPEN DATABASE FESYS. 3. Run the code from the command window. You can either paste this code to a .PRG file or paste it to the command window to run it.
#DEFINE x_CONNECTION "NameOfYourConnection" * ********************************************************************* * * * * 1/3/2001 FESYS.DBC 12:26:07 * * * ********************************************************************* * * * * Description: * * This program was automatically generated by GenDBCX Version 1.1, * * a modified version of Microsoft's utility GenDBC Version 2.26.67. * * * ********************************************************************* **************************************************

** View setup for RV_GROUPS ************************************************** Create SQL VIEW "RV_GROUPS" REMOTE CONNECTION x_CONNECTION ; AS SELECT * FROM appgroups ORDER BY appgroups.cname =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', =DBSetProp('RV_GROUPS', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'UpdateType', 1) 'WhereType', 3) 'FetchMemo', .T.) 'SendUpdates', .T.) 'UseMemoSize', 255) 'FetchSize', 100) 'MaxRecords', -1) 'Tables', 'appgroups') 'FetchSize', 100) 'Comment', "") 'BatchUpdateCount', 1) 'ShareConnection', .T.) 'Prepared', .F.) 'CompareMemo', .T.) 'FetchAsNeeded', .F.)

*!* Field Level Properties for RV_GROUPS * Props for the RV_GROUPS.cgroup_id field. =DBSetProp('RV_GROUPS.cgroup_id', 'Field', 'KeyField', .T.) =DBSetProp('RV_GROUPS.cgroup_id', 'Field', 'Updatable', .T.) =DBSetProp('RV_GROUPS.cgroup_id', 'Field', 'UpdateName', 'appgroups.cgroup_id') =DBSetProp('RV_GROUPS.cgroup_id', 'Field', 'Caption', "Group Id") =DBSetProp('RV_GROUPS.cgroup_id', 'Field', 'DataType', "C(16) NOCPTRANS") =DBSetProp('RV_GROUPS.cgroup_id', 'Field', 'DefaultValue', "GUID()") * Props for the RV_GROUPS.cname field. =DBSetProp('RV_GROUPS.cname', 'Field', 'KeyField', .F.) =DBSetProp('RV_GROUPS.cname', 'Field', 'Updatable', .T.) =DBSetProp('RV_GROUPS.cname', 'Field', 'UpdateName', 'appgroups.cname') =DBSetProp('RV_GROUPS.cname', 'Field', 'RuleExpression', "NOT EMPTY(cname)") =DBSetProp('RV_GROUPS.cname', 'Field', 'RuleText', "'Group Name must be entered.'") =DBSetProp('RV_GROUPS.cname', 'Field', 'Caption', "Group Name") =DBSetProp('RV_GROUPS.cname', 'Field', 'DataType', "C(40)") * Props for the RV_GROUPS.mdescript field. =DBSetProp('RV_GROUPS.mdescript', 'Field', 'KeyField', .F.) =DBSetProp('RV_GROUPS.mdescript', 'Field', 'Updatable', .T.) =DBSetProp('RV_GROUPS.mdescript', 'Field', 'UpdateName', 'appgroups.mdescript') =DBSetProp('RV_GROUPS.mdescript', 'Field', 'RuleExpression', "NOT EMPTY( mdescript)") =DBSetProp('RV_GROUPS.mdescript', 'Field', 'RuleText', "'Description must be entered.'") =DBSetProp('RV_GROUPS.mdescript', 'Field', 'Caption', "Description") =DBSetProp('RV_GROUPS.mdescript', 'Field', 'DataType', "M") ************************************************** ** View setup for RV_CURRENTUSER ************************************************** Local vp_cUserId vp_cUserId = "A" Create SQL VIEW "RV_CURRENTUSER" REMOTE CONNECTION x_CONNECTION ; AS SELECT * FROM appusers WHERE appusers.cuserid = ?vp_cUserId =DBSetProp('RV_CURRENTUSER', 'View', 'UpdateType', 1) =DBSetProp('RV_CURRENTUSER', 'View', 'WhereType', 3)

=DBSetProp('RV_CURRENTUSER', =DBSetProp('RV_CURRENTUSER', =DBSetProp('RV_CURRENTUSER', =DBSetProp('RV_CURRENTUSER', =DBSetProp('RV_CURRENTUSER', =DBSetProp('RV_CURRENTUSER', =DBSetProp('RV_CURRENTUSER', =DBSetProp('RV_CURRENTUSER', =DBSetProp('RV_CURRENTUSER', =DBSetProp('RV_CURRENTUSER', =DBSetProp('RV_CURRENTUSER', =DBSetProp('RV_CURRENTUSER', =DBSetProp('RV_CURRENTUSER', =DBSetProp('RV_CURRENTUSER',

'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View',

'FetchMemo', .T.) 'SendUpdates', .T.) 'UseMemoSize', 255) 'FetchSize', 100) 'MaxRecords', -1) 'Tables', 'appusers') 'FetchSize', 100) 'ParameterList', "vp_cUserId,'C'") 'Comment', "") 'BatchUpdateCount', 1) 'ShareConnection', .T.) 'Prepared', .F.) 'CompareMemo', .T.) 'FetchAsNeeded', .F.)

*!* Field Level Properties for RV_CURRENTUSER * Props for the RV_CURRENTUSER.cuserid field. =DBSetProp('RV_CURRENTUSER.cuserid', 'Field', 'KeyField', .T.) =DBSetProp('RV_CURRENTUSER.cuserid', 'Field', 'Updatable', .T.) =DBSetProp('RV_CURRENTUSER.cuserid', 'Field', 'UpdateName', 'appusers.cuserid') =DBSetProp('RV_CURRENTUSER.cuserid', 'Field', 'DataType', "C(30)") * Props for the RV_CURRENTUSER.cfirst_name field. =DBSetProp('RV_CURRENTUSER.cfirst_name', 'Field', 'KeyField', .F.) =DBSetProp('RV_CURRENTUSER.cfirst_name', 'Field', 'Updatable', .T.) =DBSetProp('RV_CURRENTUSER.cfirst_name', 'Field', 'UpdateName', 'appusers.cfirst_name') =DBSetProp('RV_CURRENTUSER.cfirst_name', 'Field', 'DataType', "C(20)") * Props for the RV_CURRENTUSER.clast_name field. =DBSetProp('RV_CURRENTUSER.clast_name', 'Field', 'KeyField', .F.) =DBSetProp('RV_CURRENTUSER.clast_name', 'Field', 'Updatable', .T.) =DBSetProp('RV_CURRENTUSER.clast_name', 'Field', 'UpdateName', 'appusers.clast_name') =DBSetProp('RV_CURRENTUSER.clast_name', 'Field', 'DataType', "C(25)") * Props for the RV_CURRENTUSER.cpassword field. =DBSetProp('RV_CURRENTUSER.cpassword', 'Field', 'KeyField', .F.) =DBSetProp('RV_CURRENTUSER.cpassword', 'Field', 'Updatable', .T.) =DBSetProp('RV_CURRENTUSER.cpassword', 'Field', 'UpdateName', 'appusers.cpassword') =DBSetProp('RV_CURRENTUSER.cpassword', 'Field', 'DataType', "C(30)") * Props for the RV_CURRENTUSER.dlastchanged field. =DBSetProp('RV_CURRENTUSER.dlastchanged', 'Field', 'KeyField', .F.) =DBSetProp('RV_CURRENTUSER.dlastchanged', 'Field', 'Updatable', .T.) =DBSetProp('RV_CURRENTUSER.dlastchanged', 'Field', 'UpdateName', 'appusers.dlastchanged') =DBSetProp('RV_CURRENTUSER.dlastchanged', 'Field', 'DataType', "D") * Props for the RV_CURRENTUSER.ladministrator field. =DBSetProp('RV_CURRENTUSER.ladministrator', 'Field', 'KeyField', .F.) =DBSetProp('RV_CURRENTUSER.ladministrator', 'Field', 'Updatable', .T.) =DBSetProp('RV_CURRENTUSER.ladministrator', 'Field', 'UpdateName', 'appusers.ladministrator') =DBSetProp('RV_CURRENTUSER.ladministrator', 'Field', 'DataType', "L") * Props for the RV_CURRENTUSER.cgroup_id field. =DBSetProp('RV_CURRENTUSER.cgroup_id', 'Field', 'KeyField', .F.) =DBSetProp('RV_CURRENTUSER.cgroup_id', 'Field', 'Updatable', .T.) =DBSetProp('RV_CURRENTUSER.cgroup_id', 'Field', 'UpdateName', 'appusers.cgroup_id') =DBSetProp('RV_CURRENTUSER.cgroup_id', 'Field', 'DataType', "C(16) NOCPTRANS") * Props for the RV_CURRENTUSER.moldpasswords field. =DBSetProp('RV_CURRENTUSER.moldpasswords', 'Field', 'KeyField', .F.) =DBSetProp('RV_CURRENTUSER.moldpasswords', 'Field', 'Updatable', .T.) =DBSetProp('RV_CURRENTUSER.moldpasswords', 'Field', 'UpdateName', 'appusers.moldpasswords')

=DBSetProp('RV_CURRENTUSER.moldpasswords', 'Field', 'DataType', "M") * Props for the RV_CURRENTUSER.linactive field. =DBSetProp('RV_CURRENTUSER.linactive', 'Field', 'KeyField', .F.) =DBSetProp('RV_CURRENTUSER.linactive', 'Field', 'Updatable', .T.) =DBSetProp('RV_CURRENTUSER.linactive', 'Field', 'UpdateName', 'appusers.linactive') =DBSetProp('RV_CURRENTUSER.linactive', 'Field', 'DataType', "L") ************************************************** ** View setup for RV_LOGIN ************************************************** Create SQL VIEW "RV_LOGIN" REMOTE CONNECTION x_CONNECTION ; AS SELECT * FROM appusers ORDER BY appusers.cuserid =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', =DBSetProp('RV_LOGIN', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'UpdateType', 1) 'WhereType', 3) 'FetchMemo', .T.) 'SendUpdates', .T.) 'UseMemoSize', 255) 'FetchSize', 100) 'MaxRecords', -1) 'Tables', 'appusers') 'FetchSize', 100) 'Comment', "") 'BatchUpdateCount', 1) 'ShareConnection', .T.) 'Prepared', .F.) 'CompareMemo', .T.) 'FetchAsNeeded', .F.)

*!* Field Level Properties for RV_LOGIN * Props for the RV_LOGIN.cuserid field. =DBSetProp('RV_LOGIN.cuserid', 'Field', 'KeyField', .T.) =DBSetProp('RV_LOGIN.cuserid', 'Field', 'Updatable', .T.) =DBSetProp('RV_LOGIN.cuserid', 'Field', 'UpdateName', 'appusers.cuserid') =DBSetProp('RV_LOGIN.cuserid', 'Field', 'DataType', "C(30)") * Props for the RV_LOGIN.cfirst_name field. =DBSetProp('RV_LOGIN.cfirst_name', 'Field', 'KeyField', .F.) =DBSetProp('RV_LOGIN.cfirst_name', 'Field', 'Updatable', .T.) =DBSetProp('RV_LOGIN.cfirst_name', 'Field', 'UpdateName', 'appusers.cfirst_name') =DBSetProp('RV_LOGIN.cfirst_name', 'Field', 'DataType', "C(20)") * Props for the RV_LOGIN.clast_name field. =DBSetProp('RV_LOGIN.clast_name', 'Field', 'KeyField', .F.) =DBSetProp('RV_LOGIN.clast_name', 'Field', 'Updatable', .T.) =DBSetProp('RV_LOGIN.clast_name', 'Field', 'UpdateName', 'appusers.clast_name') =DBSetProp('RV_LOGIN.clast_name', 'Field', 'DataType', "C(25)") * Props for the RV_LOGIN.cpassword field. =DBSetProp('RV_LOGIN.cpassword', 'Field', 'KeyField', .F.) =DBSetProp('RV_LOGIN.cpassword', 'Field', 'Updatable', .T.) =DBSetProp('RV_LOGIN.cpassword', 'Field', 'UpdateName', 'appusers.cpassword') =DBSetProp('RV_LOGIN.cpassword', 'Field', 'DataType', "C(30)") * Props for the RV_LOGIN.dlastchanged field. =DBSetProp('RV_LOGIN.dlastchanged', 'Field', 'KeyField', .F.) =DBSetProp('RV_LOGIN.dlastchanged', 'Field', 'Updatable', .T.) =DBSetProp('RV_LOGIN.dlastchanged', 'Field', 'UpdateName', 'appusers.dlastchanged') =DBSetProp('RV_LOGIN.dlastchanged', 'Field', 'DataType', "D") * Props for the RV_LOGIN.ladministrator field. =DBSetProp('RV_LOGIN.ladministrator', 'Field', 'KeyField', .F.) =DBSetProp('RV_LOGIN.ladministrator', 'Field', 'Updatable', .T.)

=DBSetProp('RV_LOGIN.ladministrator', 'Field', 'UpdateName', 'appusers.ladministrator') =DBSetProp('RV_LOGIN.ladministrator', 'Field', 'DataType', "L") * Props for the RV_LOGIN.cgroup_id field. =DBSetProp('RV_LOGIN.cgroup_id', 'Field', 'KeyField', .F.) =DBSetProp('RV_LOGIN.cgroup_id', 'Field', 'Updatable', .T.) =DBSetProp('RV_LOGIN.cgroup_id', 'Field', 'UpdateName', 'appusers.cgroup_id') =DBSetProp('RV_LOGIN.cgroup_id', 'Field', 'DataType', "C(16) NOCPTRANS") * Props for the RV_LOGIN.moldpasswords field. =DBSetProp('RV_LOGIN.moldpasswords', 'Field', 'KeyField', .F.) =DBSetProp('RV_LOGIN.moldpasswords', 'Field', 'Updatable', .T.) =DBSetProp('RV_LOGIN.moldpasswords', 'Field', 'UpdateName', 'appusers.moldpasswords') =DBSetProp('RV_LOGIN.moldpasswords', 'Field', 'DataType', "M") * Props for the RV_LOGIN.linactive field. =DBSetProp('RV_LOGIN.linactive', 'Field', 'KeyField', .F.) =DBSetProp('RV_LOGIN.linactive', 'Field', 'Updatable', .T.) =DBSetProp('RV_LOGIN.linactive', 'Field', 'UpdateName', 'appusers.linactive') =DBSetProp('RV_LOGIN.linactive', 'Field', 'DataType', "L") ************************************************** ** View setup for RV_LOGINS ************************************************** Create SQL VIEW "RV_LOGINS" REMOTE CONNECTION x_CONNECTION ; AS SELECT * FROM applogin =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', =DBSetProp('RV_LOGINS', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'UpdateType', 1) 'WhereType', 3) 'FetchMemo', .T.) 'SendUpdates', .T.) 'UseMemoSize', 255) 'FetchSize', 100) 'MaxRecords', -1) 'Tables', 'applogin') 'FetchSize', 100) 'Comment', "") 'BatchUpdateCount', 1) 'ShareConnection', .T.) 'Prepared', .F.) 'CompareMemo', .T.) 'FetchAsNeeded', .F.)

*!* Field Level Properties for RV_LOGINS * Props for the RV_LOGINS.cuserid field. =DBSetProp('RV_LOGINS.cuserid', 'Field', 'KeyField', .T.) =DBSetProp('RV_LOGINS.cuserid', 'Field', 'Updatable', .T.) =DBSetProp('RV_LOGINS.cuserid', 'Field', 'UpdateName', 'applogin.cuserid') =DBSetProp('RV_LOGINS.cuserid', 'Field', 'DataType', "C(30)") * Props for the RV_LOGINS.tloggedin field. =DBSetProp('RV_LOGINS.tloggedin', 'Field', 'KeyField', .T.) =DBSetProp('RV_LOGINS.tloggedin', 'Field', 'Updatable', .T.) =DBSetProp('RV_LOGINS.tloggedin', 'Field', 'UpdateName', 'applogin.tloggedin') =DBSetProp('RV_LOGINS.tloggedin', 'Field', 'DataType', "T") =DBSetProp('RV_LOGINS.tloggedin', 'Field', 'DefaultValue', "DATETIME()") * Props for the RV_LOGINS.tloggedout field. =DBSetProp('RV_LOGINS.tloggedout', 'Field', 'KeyField', .F.) =DBSetProp('RV_LOGINS.tloggedout', 'Field', 'Updatable', .T.) =DBSetProp('RV_LOGINS.tloggedout', 'Field', 'UpdateName', 'applogin.tloggedout') =DBSetProp('RV_LOGINS.tloggedout', 'Field', 'DataType', "T")

* Props for the RV_LOGINS.tcleared field. =DBSetProp('RV_LOGINS.tcleared', 'Field', 'KeyField', .F.) =DBSetProp('RV_LOGINS.tcleared', 'Field', 'Updatable', .T.) =DBSetProp('RV_LOGINS.tcleared', 'Field', 'UpdateName', 'applogin.tcleared') =DBSetProp('RV_LOGINS.tcleared', 'Field', 'DataType', "T") * Props for the RV_LOGINS.cclearedby field. =DBSetProp('RV_LOGINS.cclearedby', 'Field', 'KeyField', .F.) =DBSetProp('RV_LOGINS.cclearedby', 'Field', 'Updatable', .T.) =DBSetProp('RV_LOGINS.cclearedby', 'Field', 'UpdateName', 'applogin.cclearedby') =DBSetProp('RV_LOGINS.cclearedby', 'Field', 'DataType', "C(30)") ************************************************** ** View setup for RV_APPINFO ************************************************** Create SQL VIEW "RV_APPINFO" REMOTE CONNECTION x_CONNECTION ; AS SELECT * FROM appinfo =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', =DBSetProp('RV_APPINFO', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'UpdateType', 1) 'WhereType', 3) 'FetchMemo', .T.) 'SendUpdates', .T.) 'UseMemoSize', 255) 'FetchSize', 100) 'MaxRecords', -1) 'Tables', 'appinfo') 'FetchSize', 100) 'Comment', "") 'BatchUpdateCount', 1) 'ShareConnection', .T.) 'Prepared', .F.) 'CompareMemo', .T.) 'FetchAsNeeded', .F.)

*!* Field Level Properties for RV_APPINFO * Props for the RV_APPINFO.cid field. =DBSetProp('RV_APPINFO.cid', 'Field', 'KeyField', .T.) =DBSetProp('RV_APPINFO.cid', 'Field', 'Updatable', .T.) =DBSetProp('RV_APPINFO.cid', 'Field', 'UpdateName', 'appinfo.cid') =DBSetProp('RV_APPINFO.cid', 'Field', 'DataType', "C(16) NOCPTRANS") =DBSetProp('RV_APPINFO.cid', 'Field', 'DefaultValue', "GUID()") =DBSetProp('RV_APPINFO.cid', 'Field', 'DisplayClass', "cactivedoc") =DBSetProp('RV_APPINFO.cid', 'Field', 'DisplayClassLibrary', "c:\vfe98\vfeframe\libs\ccontrls.vcx") * Props for the RV_APPINFO.nminpasswordlength field. =DBSetProp('RV_APPINFO.nminpasswordlength', 'Field', 'KeyField', .F.) =DBSetProp('RV_APPINFO.nminpasswordlength', 'Field', 'Updatable', .T.) =DBSetProp('RV_APPINFO.nminpasswordlength', 'Field', 'UpdateName', 'appinfo.nminpasswordlength') =DBSetProp('RV_APPINFO.nminpasswordlength', 'Field', 'DataType', "N(2)") =DBSetProp('RV_APPINFO.nminpasswordlength', 'Field', 'DefaultValue', "4") =DBSetProp('RV_APPINFO.nminpasswordlength', 'Field', 'DisplayClass', "cspinner") =DBSetProp('RV_APPINFO.nminpasswordlength', 'Field', 'DisplayClassLibrary', "c:\vfe98\vfeframe\libs\ccontrls.vcx") * Props for the RV_APPINFO.nminuseridlength field. =DBSetProp('RV_APPINFO.nminuseridlength', 'Field', 'KeyField', .F.) =DBSetProp('RV_APPINFO.nminuseridlength', 'Field', 'Updatable', .T.) =DBSetProp('RV_APPINFO.nminuseridlength', 'Field', 'UpdateName', 'appinfo.nminuseridlength') =DBSetProp('RV_APPINFO.nminuseridlength', 'Field', 'DataType', "N(2)")

=DBSetProp('RV_APPINFO.nminuseridlength', 'Field', 'DefaultValue', "3") =DBSetProp('RV_APPINFO.nminuseridlength', 'Field', 'DisplayClass', "cspinner") =DBSetProp('RV_APPINFO.nminuseridlength', 'Field', 'DisplayClassLibrary', "c:\vfe98\vfeframe\libs\ccontrls.vcx") * Props for the RV_APPINFO.nmaxlogins field. =DBSetProp('RV_APPINFO.nmaxlogins', 'Field', 'KeyField', .F.) =DBSetProp('RV_APPINFO.nmaxlogins', 'Field', 'Updatable', .T.) =DBSetProp('RV_APPINFO.nmaxlogins', 'Field', 'UpdateName', 'appinfo.nmaxlogins') =DBSetProp('RV_APPINFO.nmaxlogins', 'Field', 'DataType', "N(2)") =DBSetProp('RV_APPINFO.nmaxlogins', 'Field', 'DefaultValue', "1") =DBSetProp('RV_APPINFO.nmaxlogins', 'Field', 'DisplayClass', "cspinner") =DBSetProp('RV_APPINFO.nmaxlogins', 'Field', 'DisplayClassLibrary', "c:\vfe98\vfeframe\libs\ccontrls.vcx") * Props for the RV_APPINFO.nfailures field. =DBSetProp('RV_APPINFO.nfailures', 'Field', 'KeyField', .F.) =DBSetProp('RV_APPINFO.nfailures', 'Field', 'Updatable', .T.) =DBSetProp('RV_APPINFO.nfailures', 'Field', 'UpdateName', 'appinfo.nfailures') =DBSetProp('RV_APPINFO.nfailures', 'Field', 'DataType', "N(2)") =DBSetProp('RV_APPINFO.nfailures', 'Field', 'DefaultValue', "3") =DBSetProp('RV_APPINFO.nfailures', 'Field', 'DisplayClass', "cspinner") =DBSetProp('RV_APPINFO.nfailures', 'Field', 'DisplayClassLibrary', "c:\vfe98\vfeframe\libs\ccontrls.vcx") * Props for the RV_APPINFO.luniquepassword field. =DBSetProp('RV_APPINFO.luniquepassword', 'Field', 'KeyField', .F.) =DBSetProp('RV_APPINFO.luniquepassword', 'Field', 'Updatable', .T.) =DBSetProp('RV_APPINFO.luniquepassword', 'Field', 'UpdateName', 'appinfo.luniquepassword') =DBSetProp('RV_APPINFO.luniquepassword', 'Field', 'DataType', "L") =DBSetProp('RV_APPINFO.luniquepassword', 'Field', 'DefaultValue', ".T.") =DBSetProp('RV_APPINFO.luniquepassword', 'Field', 'DisplayClass', "ccheckbox") =DBSetProp('RV_APPINFO.luniquepassword', 'Field', 'DisplayClassLibrary', "c:\vfe98\vfeframe\libs\ccontrls.vcx") * Props for the RV_APPINFO.nchangepassword field. =DBSetProp('RV_APPINFO.nchangepassword', 'Field', 'KeyField', .F.) =DBSetProp('RV_APPINFO.nchangepassword', 'Field', 'Updatable', .T.) =DBSetProp('RV_APPINFO.nchangepassword', 'Field', 'UpdateName', 'appinfo.nchangepassword') =DBSetProp('RV_APPINFO.nchangepassword', 'Field', 'DataType', "N(4)") =DBSetProp('RV_APPINFO.nchangepassword', 'Field', 'DefaultValue', "0") =DBSetProp('RV_APPINFO.nchangepassword', 'Field', 'DisplayClass', "cspinner") =DBSetProp('RV_APPINFO.nchangepassword', 'Field', 'DisplayClassLibrary', "c:\vfe98\vfeframe\libs\ccontrls.vcx") * Props for the RV_APPINFO.ndefaultlevel field. =DBSetProp('RV_APPINFO.ndefaultlevel', 'Field', 'KeyField', .F.) =DBSetProp('RV_APPINFO.ndefaultlevel', 'Field', 'Updatable', .T.) =DBSetProp('RV_APPINFO.ndefaultlevel', 'Field', 'UpdateName', 'appinfo.ndefaultlevel') =DBSetProp('RV_APPINFO.ndefaultlevel', 'Field', 'DataType', "N(1)") =DBSetProp('RV_APPINFO.ndefaultlevel', 'Field', 'DefaultValue', "1") =DBSetProp('RV_APPINFO.ndefaultlevel', 'Field', 'DisplayClass', "coptiongroup") =DBSetProp('RV_APPINFO.ndefaultlevel', 'Field', 'DisplayClassLibrary', "c:\vfe98\vfeframe\libs\ccontrls.vcx") ************************************************** ** View setup for RV_SECURITYNAMES ************************************************** Create SQL VIEW "RV_SECURITYNAMES" REMOTE CONNECTION x_CONNECTION ; AS SELECT * FROM appnames

=DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES', =DBSetProp('RV_SECURITYNAMES',

'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View',

'UpdateType', 1) 'WhereType', 3) 'FetchMemo', .T.) 'SendUpdates', .T.) 'UseMemoSize', 255) 'FetchSize', 100) 'MaxRecords', -1) 'Tables', 'appnames') 'FetchSize', 100) 'Comment', "") 'BatchUpdateCount', 1) 'ShareConnection', .T.) 'Prepared', .F.) 'CompareMemo', .T.) 'FetchAsNeeded', .F.)

*!* Field Level Properties for RV_SECURITYNAMES * Props for the RV_SECURITYNAMES.cname field. =DBSetProp('RV_SECURITYNAMES.cname', 'Field', 'KeyField', .T.) =DBSetProp('RV_SECURITYNAMES.cname', 'Field', 'Updatable', .T.) =DBSetProp('RV_SECURITYNAMES.cname', 'Field', 'UpdateName', 'appnames.cname') =DBSetProp('RV_SECURITYNAMES.cname', 'Field', 'DataType', "C(40)") * Props for the RV_SECURITYNAMES.mdescription field. =DBSetProp('RV_SECURITYNAMES.mdescription', 'Field', 'KeyField', .F.) =DBSetProp('RV_SECURITYNAMES.mdescription', 'Field', 'Updatable', .T.) =DBSetProp('RV_SECURITYNAMES.mdescription', 'Field', 'UpdateName', 'appnames.mdescription') =DBSetProp('RV_SECURITYNAMES.mdescription', 'Field', 'DataType', "M") ************************************************** ** View setup for RV_RIGHTS ************************************************** Create SQL VIEW "RV_RIGHTS" REMOTE CONNECTION x_CONNECTION ; AS SELECT * FROM apprites WHERE apprites.cgroup_id = ?v_groups.cGroup_Id =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', =DBSetProp('RV_RIGHTS', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'UpdateType', 1) 'WhereType', 3) 'FetchMemo', .T.) 'SendUpdates', .T.) 'UseMemoSize', 255) 'FetchSize', 100) 'MaxRecords', -1) 'Tables', 'apprites') 'FetchSize', 100) 'Comment', "") 'BatchUpdateCount', 1) 'ShareConnection', .T.) 'Prepared', .F.) 'CompareMemo', .T.) 'FetchAsNeeded', .F.)

*!* Field Level Properties for RV_RIGHTS * Props for the RV_RIGHTS.cgroup_id field. =DBSetProp('RV_RIGHTS.cgroup_id', 'Field', 'KeyField', .T.) =DBSetProp('RV_RIGHTS.cgroup_id', 'Field', 'Updatable', .T.) =DBSetProp('RV_RIGHTS.cgroup_id', 'Field', 'UpdateName', 'apprites.cgroup_id') =DBSetProp('RV_RIGHTS.cgroup_id', 'Field', 'DataType', "C(16) NOCPTRANS") * Props for the RV_RIGHTS.cname field. =DBSetProp('RV_RIGHTS.cname', 'Field', 'KeyField', .T.)

=DBSetProp('RV_RIGHTS.cname', 'Field', 'Updatable', .T.) =DBSetProp('RV_RIGHTS.cname', 'Field', 'UpdateName', 'apprites.cname') =DBSetProp('RV_RIGHTS.cname', 'Field', 'DataType', "C(40)") * Props for the RV_RIGHTS.nrights field. =DBSetProp('RV_RIGHTS.nrights', 'Field', 'KeyField', .F.) =DBSetProp('RV_RIGHTS.nrights', 'Field', 'Updatable', .T.) =DBSetProp('RV_RIGHTS.nrights', 'Field', 'UpdateName', 'apprites.nrights') =DBSetProp('RV_RIGHTS.nrights', 'Field', 'DataType', "N(1)") ************************************************** ** View setup for RV_USERS ************************************************** Create SQL VIEW "RV_USERS" REMOTE CONNECTION x_CONNECTION ; AS SELECT * FROM appusers =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', =DBSetProp('RV_USERS', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'UpdateType', 1) 'WhereType', 3) 'FetchMemo', .T.) 'SendUpdates', .T.) 'UseMemoSize', 255) 'FetchSize', 100) 'MaxRecords', -1) 'Tables', 'appusers') 'FetchSize', 100) 'Comment', "") 'BatchUpdateCount', 1) 'ShareConnection', .T.) 'Prepared', .F.) 'CompareMemo', .T.) 'FetchAsNeeded', .F.)

*!* Field Level Properties for RV_USERS * Props for the RV_USERS.cuserid field. =DBSetProp('RV_USERS.cuserid', 'Field', 'KeyField', .T.) =DBSetProp('RV_USERS.cuserid', 'Field', 'Updatable', .T.) =DBSetProp('RV_USERS.cuserid', 'Field', 'UpdateName', 'appusers.cuserid') =DBSetProp('RV_USERS.cuserid', 'Field', 'DataType', "C(30)") * Props for the RV_USERS.cfirst_name field. =DBSetProp('RV_USERS.cfirst_name', 'Field', 'KeyField', .F.) =DBSetProp('RV_USERS.cfirst_name', 'Field', 'Updatable', .T.) =DBSetProp('RV_USERS.cfirst_name', 'Field', 'UpdateName', 'appusers.cfirst_name') =DBSetProp('RV_USERS.cfirst_name', 'Field', 'DataType', "C(20)") * Props for the RV_USERS.clast_name field. =DBSetProp('RV_USERS.clast_name', 'Field', 'KeyField', .F.) =DBSetProp('RV_USERS.clast_name', 'Field', 'Updatable', .T.) =DBSetProp('RV_USERS.clast_name', 'Field', 'UpdateName', 'appusers.clast_name') =DBSetProp('RV_USERS.clast_name', 'Field', 'DataType', "C(25)") * Props for the RV_USERS.cpassword field. =DBSetProp('RV_USERS.cpassword', 'Field', 'KeyField', .F.) =DBSetProp('RV_USERS.cpassword', 'Field', 'Updatable', .T.) =DBSetProp('RV_USERS.cpassword', 'Field', 'UpdateName', 'appusers.cpassword') =DBSetProp('RV_USERS.cpassword', 'Field', 'DataType', "C(30)") * Props for the RV_USERS.dlastchanged field. =DBSetProp('RV_USERS.dlastchanged', 'Field', 'KeyField', .F.) =DBSetProp('RV_USERS.dlastchanged', 'Field', 'Updatable', .T.) =DBSetProp('RV_USERS.dlastchanged', 'Field', 'UpdateName', 'appusers.dlastchanged') =DBSetProp('RV_USERS.dlastchanged', 'Field', 'DataType', "D") * Props for the RV_USERS.ladministrator field. =DBSetProp('RV_USERS.ladministrator', 'Field', 'KeyField', .F.)

=DBSetProp('RV_USERS.ladministrator', 'Field', 'Updatable', .T.) =DBSetProp('RV_USERS.ladministrator', 'Field', 'UpdateName', 'appusers.ladministrator') =DBSetProp('RV_USERS.ladministrator', 'Field', 'DataType', "L") * Props for the RV_USERS.cgroup_id field. =DBSetProp('RV_USERS.cgroup_id', 'Field', 'KeyField', .F.) =DBSetProp('RV_USERS.cgroup_id', 'Field', 'Updatable', .T.) =DBSetProp('RV_USERS.cgroup_id', 'Field', 'UpdateName', 'appusers.cgroup_id') =DBSetProp('RV_USERS.cgroup_id', 'Field', 'DataType', "C(16) NOCPTRANS") * Props for the RV_USERS.moldpasswords field. =DBSetProp('RV_USERS.moldpasswords', 'Field', 'KeyField', .F.) =DBSetProp('RV_USERS.moldpasswords', 'Field', 'Updatable', .T.) =DBSetProp('RV_USERS.moldpasswords', 'Field', 'UpdateName', 'appusers.moldpasswords') =DBSetProp('RV_USERS.moldpasswords', 'Field', 'DataType', "M") * Props for the RV_USERS.linactive field. =DBSetProp('RV_USERS.linactive', 'Field', 'KeyField', .F.) =DBSetProp('RV_USERS.linactive', 'Field', 'Updatable', .T.) =DBSetProp('RV_USERS.linactive', 'Field', 'UpdateName', 'appusers.linactive') =DBSetProp('RV_USERS.linactive', 'Field', 'DataType', "L") ************************************************** ** View setup for RV_RIGHTSTONAME ************************************************** Local vp_cGroup_id,vp_cName vp_cGroup_id = "A" vp_cName = "A" Create SQL VIEW "RV_RIGHTSTONAME" REMOTE CONNECTION x_CONNECTION ; AS SELECT * FROM apprites WHERE apprites.cgroup_id = ?vp_cGroup_id AND UPPER(apprites.cname) = UPPER(?vp_cName) =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', "vp_cGroup_id,'C';vp_cName,'C'") =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', =DBSetProp('RV_RIGHTSTONAME', 'View', 'UpdateType', 1) 'WhereType', 3) 'FetchMemo', .T.) 'SendUpdates', .F.) 'UseMemoSize', 255) 'FetchSize', 100) 'MaxRecords', -1) 'Tables', '') 'FetchSize', 100) 'ParameterList', 'Comment', "") 'BatchUpdateCount', 1) 'ShareConnection', .T.) 'Prepared', .F.) 'CompareMemo', .T.) 'FetchAsNeeded', .F.)

*!* Field Level Properties for RV_RIGHTSTONAME * Props for the RV_RIGHTSTONAME.cgroup_id field. =DBSetProp('RV_RIGHTSTONAME.cgroup_id', 'Field', 'KeyField', .T.) =DBSetProp('RV_RIGHTSTONAME.cgroup_id', 'Field', 'Updatable', .F.) =DBSetProp('RV_RIGHTSTONAME.cgroup_id', 'Field', 'UpdateName', 'apprites.cgroup_id') =DBSetProp('RV_RIGHTSTONAME.cgroup_id', 'Field', 'DataType', "C(16) NOCPTRANS") * Props for the RV_RIGHTSTONAME.cname field. =DBSetProp('RV_RIGHTSTONAME.cname', 'Field', 'KeyField', .T.)

=DBSetProp('RV_RIGHTSTONAME.cname', 'Field', 'Updatable', .F.) =DBSetProp('RV_RIGHTSTONAME.cname', 'Field', 'UpdateName', 'apprites.cname') =DBSetProp('RV_RIGHTSTONAME.cname', 'Field', 'DataType', "C(40)") * Props for the RV_RIGHTSTONAME.nrights field. =DBSetProp('RV_RIGHTSTONAME.nrights', 'Field', 'KeyField', .F.) =DBSetProp('RV_RIGHTSTONAME.nrights', 'Field', 'Updatable', .F.) =DBSetProp('RV_RIGHTSTONAME.nrights', 'Field', 'UpdateName', 'apprites.nrights') =DBSetProp('RV_RIGHTSTONAME.nrights', 'Field', 'DataType', "N(1)") ************************************************** ** View setup for RV_GROUPRIGHTS ************************************************** Local vp_cGroup_Id vp_cGroup_Id = "A" Create SQL VIEW "RV_GROUPRIGHTS" REMOTE CONNECTION x_CONNECTION ; AS SELECT * FROM apprites WHERE apprites.cgroup_id = ?vp_cGroup_Id =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', =DBSetProp('RV_GROUPRIGHTS', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'UpdateType', 1) 'WhereType', 3) 'FetchMemo', .T.) 'SendUpdates', .T.) 'UseMemoSize', 255) 'FetchSize', 100) 'MaxRecords', -1) 'Tables', 'apprites') 'FetchSize', 100) 'ParameterList', "vp_cGroup_Id,'C'") 'Comment', "") 'BatchUpdateCount', 1) 'ShareConnection', .T.) 'Prepared', .F.) 'CompareMemo', .T.) 'FetchAsNeeded', .F.)

*!* Field Level Properties for RV_GROUPRIGHTS * Props for the RV_GROUPRIGHTS.cgroup_id field. =DBSetProp('RV_GROUPRIGHTS.cgroup_id', 'Field', 'KeyField', .T.) =DBSetProp('RV_GROUPRIGHTS.cgroup_id', 'Field', 'Updatable', .T.) =DBSetProp('RV_GROUPRIGHTS.cgroup_id', 'Field', 'UpdateName', 'apprites.cgroup_id') =DBSetProp('RV_GROUPRIGHTS.cgroup_id', 'Field', 'DataType', "C(16) NOCPTRANS") * Props for the RV_GROUPRIGHTS.cname field. =DBSetProp('RV_GROUPRIGHTS.cname', 'Field', 'KeyField', .T.) =DBSetProp('RV_GROUPRIGHTS.cname', 'Field', 'Updatable', .T.) =DBSetProp('RV_GROUPRIGHTS.cname', 'Field', 'UpdateName', 'apprites.cname') =DBSetProp('RV_GROUPRIGHTS.cname', 'Field', 'DataType', "C(40)") * Props for the RV_GROUPRIGHTS.nrights field. =DBSetProp('RV_GROUPRIGHTS.nrights', 'Field', 'KeyField', .F.) =DBSetProp('RV_GROUPRIGHTS.nrights', 'Field', 'Updatable', .T.) =DBSetProp('RV_GROUPRIGHTS.nrights', 'Field', 'UpdateName', 'apprites.nrights') =DBSetProp('RV_GROUPRIGHTS.nrights', 'Field', 'DataType', "N(1)") ************************************************** ** View setup for RV_CURRENTLOGINS ************************************************** Local vp_cUserId vp_cUserId = "A"

Create SQL VIEW "RV_CURRENTLOGINS" REMOTE CONNECTION x_CONNECTION ; AS SELECT * FROM applogin WHERE applogin.cuserid = ?vp_cUserId AND applogin.tloggedout IS NULL AND applogin.tcleared IS NULL ORDER BY applogin.tloggedin DESC =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', =DBSetProp('RV_CURRENTLOGINS', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'View', 'UpdateType', 1) 'WhereType', 3) 'FetchMemo', .T.) 'SendUpdates', .T.) 'UseMemoSize', 255) 'FetchSize', 100) 'MaxRecords', -1) 'Tables', 'applogin') 'FetchSize', 100) 'ParameterList', "vp_cUserId,'C'") 'Comment', "") 'BatchUpdateCount', 1) 'ShareConnection', .T.) 'Prepared', .F.) 'CompareMemo', .T.) 'FetchAsNeeded', .F.)

*!* Field Level Properties for RV_CURRENTLOGINS * Props for the RV_CURRENTLOGINS.cuserid field. =DBSetProp('RV_CURRENTLOGINS.cuserid', 'Field', 'KeyField', .T.) =DBSetProp('RV_CURRENTLOGINS.cuserid', 'Field', 'Updatable', .F.) =DBSetProp('RV_CURRENTLOGINS.cuserid', 'Field', 'UpdateName', 'applogin.cuserid') =DBSetProp('RV_CURRENTLOGINS.cuserid', 'Field', 'DataType', "C(30)") * Props for the RV_CURRENTLOGINS.tloggedin field. =DBSetProp('RV_CURRENTLOGINS.tloggedin', 'Field', 'KeyField', .T.) =DBSetProp('RV_CURRENTLOGINS.tloggedin', 'Field', 'Updatable', .F.) =DBSetProp('RV_CURRENTLOGINS.tloggedin', 'Field', 'UpdateName', 'applogin.tloggedin') =DBSetProp('RV_CURRENTLOGINS.tloggedin', 'Field', 'DataType', "T") * Props for the RV_CURRENTLOGINS.tloggedout field. =DBSetProp('RV_CURRENTLOGINS.tloggedout', 'Field', 'KeyField', .F.) =DBSetProp('RV_CURRENTLOGINS.tloggedout', 'Field', 'Updatable', .T.) =DBSetProp('RV_CURRENTLOGINS.tloggedout', 'Field', 'UpdateName', 'applogin.tloggedout') =DBSetProp('RV_CURRENTLOGINS.tloggedout', 'Field', 'DataType', "T") * Props for the RV_CURRENTLOGINS.tcleared field. =DBSetProp('RV_CURRENTLOGINS.tcleared', 'Field', 'KeyField', .F.) =DBSetProp('RV_CURRENTLOGINS.tcleared', 'Field', 'Updatable', .T.) =DBSetProp('RV_CURRENTLOGINS.tcleared', 'Field', 'UpdateName', 'applogin.tcleared') =DBSetProp('RV_CURRENTLOGINS.tcleared', 'Field', 'DataType', "T") * Props for the RV_CURRENTLOGINS.cclearedby field. =DBSetProp('RV_CURRENTLOGINS.cclearedby', 'Field', 'KeyField', .F.) =DBSetProp('RV_CURRENTLOGINS.cclearedby', 'Field', 'Updatable', .T.) =DBSetProp('RV_CURRENTLOGINS.cclearedby', 'Field', 'UpdateName', 'applogin.cclearedby') =DBSetProp('RV_CURRENTLOGINS.cclearedby', 'Field', 'DataType', "C(30)")

After you run the above code and have also created the tables on the database, you should be able to USE these views. This is also how you can move the data from the local tables to the remote tables. If you simply use the views then you can append from the tables to the views, and issue a tableupdate() command.

Keep in mind that foreign key constraints are set up, so you will need to put the parent data before you put the child data. This means you must put groups on the server before you put users. You must also put appnames on the server before you put the apprights.

Gotchas

While this process worked a lot better than I expected there are a few issues that you should be aware of, but none of them are show-stoppers in my opinion.

Login Journal
Since SQL Server doesnt allow for empty dates, I set the logout and cleared dates to allow nulls. However, when you use the Purge button on the login journal of the login screen ALL records are erased. This is happening because the code basically says, delete all records where cleared or logout is not empty. Well, as we all know, NULL is not equal to empty, so all records are matched. I have reported this as a bug, but it is easy enough to fix yourself.

Do what I say, not what I do


Well, with all the awesome power of VFE, and all the time it saves me, I guess I can forgive this one. But, basically, the IDE is NOT n-tier. As a matter of fact, when you add a security name in the IDE, it doesnt even open the VIEW to add the name, it opens the table directly. What this means is that you have to leave the security tables around on your development machine. As you add security names in DBCX Explorer and the wizards VFE will add those names to the appname.dbf file. Its a small problem, once you are done developing just move the data to the remote table of the same name. As a matter of fact, you can write a quick PRG to do this for you. I wont insult your intelligence by providing it here. (Actually I would if I had written it yet.)

i-Layer Security Classes


Well, the truth is, very few of the security classes are in the iLibs. So, if you want to extend the use tables, add your own attributes to users, etc, you will have to modify some of the c-Layer classes.

Login form creates the security object?


What? Yes, I dont know why, but this is how VFE works. Instead of the application object instantiating the security object, and having the security object run the login form if no user information was provided the login form is called, which creates the security object and attaches it to the application object. The main problem here is that we wanted to be able to put up an SQL like login form, and once the connection was made pass the user/password info to the security object. Then we could tell our users to set up the same username/password for the application that the users have on the SQL server. This would work sort of the way windows does, if you use the same Windows username and password as your network, the login info is passed, and if it is valid you never see the second login form. The second problem is that you have an Unknown data session hanging around. Because the security tables are opened up under the login forms private data session, but the security object is created in the default data session. So the security object has a reference to the cursor class which was created in the login forms session. Once the form goes away, the session changes to Unknown. There isnt really a problem with this, it is just a bit of a kludge.

Were on it
The folks at F1 have promised that they know there are issues with the security system and will address it ASAP. We hope to se some security enhancements in service pack 3. At least creating I-layer classes and using those and the factory object so you wont have to modify c-layer classes to customize the security.

Summary

If you are going to use a SQL Server database and one of its benefits is security, dont loose that benefit. I have done most of the leg work for you here. All you need to do is make some backups, run a few scripts, and give it a go.

You might also like