Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Articles
Search: 

Data dictionary and the validation
Michel Fournier, January 1, 2006
Data dictionaries has its use and also for Web applications. I see many developers building Web applications who forget about many structured that used to be in place when developping desktop applications. The same should apply for Web applications as it is no different. This article discusses some ...
Summary
Data dictionaries has its use and also for Web applications. I see many developers building Web applications who forget about many structured that used to be in place when developping desktop applications. The same should apply for Web applications as it is no different. This article discusses some data dictionary techniques in regards to validation of HTML form controls.
Description
Data dictionaries has its use and also for Web applications. I see many developers building Web applications who forget about many structures that used to be in place when developping desktop applications. The same should apply for Web applications as it is no different. This article discusses some data dictionary techniques in regards to validation of HTML form controls.

...after having built Web applications for more than 10 years now, I can say that I have faced all kind of situations, where some of them include users who just try to enter about everything in a field, data that does not make any sense and does not relate into your application.

Types of data dictionaries

Data dictionaries is simply a terminology used for specific application data that is used to control how your application works. This terminology has several other synonyms that different development team could use. The main goal is to rely on data sets which helps you control business logics and other related needs in your application.

At minimum, I like to rely on a small set of tables to control the list of tables I have in my application and related data, the field validations and the relationships between each of them. In this article, I discuss the use of the field validation table to control HTML forms submitted by the user.

Benefits of data dictionaries

Properties
Methods
Some may ask why we should use such an approach or why is it proposed. At first, all the validations used to be in the code. It was providing the developers an easy approach to update the application to add additional validations by simply going in one place and making the customized adjustments for the required needs. But, as the developers are adding more and more validation rules into their applications, they find themselves in the situation that common validation rules are used in several places. So, they realized that the same type of tasks was required to add those validations rules in several forms.

So, those were the old days. Then came the venue of using a data dictionary to handle those requirements. This is when the field table is used, where among other things, it contains whatever is necessary to provide the minimum capabilities in your application to support those validation rules.

One thing I realized over the years, when switching from desktop to Web applications, is the amount of validation rules in place is usually much bigger for the Web applications. In a desktop environment, most of the times, we know the users. In a Web environment, most of the times, the application is public. Thus, we face the situation that we have, most of the times, much more users using the application and for most of them, we simply do not know who they are. So, there is usually a need to add more validations into the application, in order to make it more robust and more secure. From experience, after having built Web applications for more than 10 years now, I can say that I have faced all kind of situations, where some of them include users who just try to enter about everything in a field, data that does not make any sense and does not relate into your application. One reason is simply because many individuals just do not want to give personal information online. Another one is simply because they have bad intentions. Whatever the reason is, the more validation rules you have in place, the better your application will be.

So, this brings us to the point where we can use a table to gather the related data about those required validations. This is really nice because you can add validation rules for a specific field without changing anything in your code. Some common validation rules include validations such as the desire to validate a field to avoid empty values, to make sure the field is not saved in uppercase, to validate a province with a country and to validate a numeric field into a specific range. This article discusses the use of over 30 validation rules. It does not cover all of them nor will it ever be. No matter as generic as you could try to make it, there will always be specific validation rules that will need to be inside your code as they are really customized into the business logic of your application. But, this should give you a good start or provide you some good tips on how you can enhance your actual infrastructure in place for such needs.

The field validation table

The field validation table holds information about specific fields of various tables that are used in the application at the validation level. Not all of the fields of your tables should be defined in here. But, if some are there, they will be processed when time comes.

That table structure is as followed:

Field name Type Width Details
Numero Integer   Primary key
AddUser Integer   Primary key of the member table of the member who created this record
AddDate DateTime   Date and time of the record creation
ModUser Integer   Primary key of the member table of the member who last updated this record
ModDate DateTime   Date and time of the last update
NoTable Integer   Primary key of the table relationship
Field Character 10 Field name
Mandatory Logical   If the field is mandatory
Uppercase Logical   If the field cannot be in uppercase
PutUp Logical   If the field will be converted with the PutUp() function at creation time. This function applies something similar to the Proper() function but more customized for local needs.
PutUpMod Logical   If the field will be converted with the PutUp() function at update time
Email Logical   If the field is an email. When this is the case, several validations will kick in to assure the value as minimum requirements to be an email. The validation does not check to see if the email is valid however. The validation is done by calling the ValidationEmail() object which is dedicated for such validation. Within this object, it is assumed the object is already created under goValidationEmail.
SaveUpper Logical   If the field will be saved in uppercase
URL Logical   If the field is an URL. It will be saved in lowercase. The value will also be converted to a proper URL is the start of the URL is invalid.
Tag Logical   If the field cannot contain any HTML tag. This is mostly used for memo field when being used in a TEXTAREA tag.
Style Logical   If the field cannot contain any HTML tag parameter. This is mostly used for memo field when being used in a TEXTAREA tag and where specific tag parameters cannot be accepted.
Obscenity Logical   If the field cannot contain obscenities. Some basic obsenities will be checked for.
SaveLower Logical   If the field will be saved in lowercase
Province Logical   If the field is a province. When this is the case, the combination Province/Canada and States/United States will be validated. There is a naming conventation in the application which establishes a relationship between a province/state field and the country. The field is called NoProvince, which has a relationship with Province.dbf table, and its country will be called NoCountry, which has a relationship with Country.dbf. Thus, is a field in a HTML form is named NoProvince, we can assume the form also contains a field named NoCountry. The validation is also supporting more than one NoProvince field in a table. So, if you have an address for the member's information and one for the payment, for exemple, you can use NoProvince2 and the validation will search for NoCountry2 to establish this relationship for the validation process.
Unique Logical   If the field has to be unique. This is really useful to make sure the value is unique in our table.
Code Logical   If the field is a postal code or a zip code. Canadian postal codes and United States zip code validations will kick in.
Anglais Memo   User friendly field name in English
French Memo   User friendly field name in French
Espagnol Memo   User friendly field name in Spanish
Portuga Memo   User friendly field name in Portuguese
Admin Logical   If the field will not be validated in administration mode. By default, a field is validated no matter where used. However, sometimes, when being used in a HTML form from the administration mode, we may not want the validation to kick in. In this case, the field will be .T. When combined with the recognition of the lAdmin property set to .T., the field will not be validated.
CharReject Character 20 List of characters which would not be accepted. There is no delimiter. Each character is entered one after the other.
Initial Logical   If we do not want to accept an initial for character fields. This applies when there is only one character or one character followed by a dot.
StringNot Memo   List of strings we do not want to accept for character or memo fields. Each string is delimited by a comma.
MinLength Numeric 3 Minimum length of the value for memo fields
NumericF Logical   If the field is numeric. This should be .T. when we use NumericLo and NumericLo.
NumericLo Numeric 8 Minimum value to accept when the field is defined numeric.
NumericHi Numeric 8 Maximum value to accept when the field is defined numeric.
ValueNot Memo   List of values we do not want to accept for character or memo fields. Each string is delimited by a comma.
SameKey Logical   If we do not want to accept a value which would only be a repetition of the same key. This applies when the field is character or memo.
ZeroValue Logical   If we accept a value of zero. This is useful when we are using NumericLo and NumericHi.
MaxLength Numeric 6 Maximum length of the value for memo fields
CreditCard Logical   If the field is a credit card number

The Validation object

I have defined a Validation object which allows me to set some property values, used for validation process, before calling the ValidationFromField() method. The object is defined as followed:

PropertiesDetails
aMessage[35,4] Array of messages
cAlias Alias
cEmail Email address
cField Field name to be used in validation messages
cType Field type
cValidationMessage Message of validation
lAdmin If we are in administration mode
nErrorValidation Error number of the validation message
nNumero Primary key of the record to validate
xValue Value of the HTML field

MethodsDetails
Validate() Execute the validation rules
ValidateEmail() Validation of an email

The Init() method

The Init() method contains the definition of all validation messages in English, French, Portuguese and Spanish. Those messages have been removed from the Message.dbf table in order to provide ready to go code whenever this is used on a new server. It allows us to benefit of a validation object without having to rely on a Message.dbf table and make sure the proper records are inserted. The Message.dbf table is usually used to provide string access by the use of a data dictionary to be able to maintain the string content directly from a table and to support foreign interfaces.

Some strings in foreign languages are not translated. Thus, they are holding the English values for now. This can be easily adjusted by simply adjusting the proper messages.

* 1 We have some tags which are not in PRE
* 2 We have a A tag but it contains something else than HREF
* 3 We have a tag which contains a style
* 4 We have a FONT tag which contains something else than SIZE, COLOR and FACE
* 5 We have some obscenities
* 6 The field cannot be empty
* 7 The field cannot be in uppercase
* 8 The email cannot be blank
* 9 You need to have at least one dot in your email
* 10 You need to have at least one @ in your email
* 11 You cannot have an email starting with @
* 12 The last character of your email cannot be a dot
* 13 You cannot have more than one @ in your email
* 14 You cannot have a space in the email
* 15 You cannot have a comma in the email
* 16 The email is not valid
* 17 You need to have at least five characters in your email
* 18 After the last dot, you cannot have a number in your email
* 19 You cannot have a dot after the @ in the email
* 20 You cannot have a semi colon in the email
* 21 The country and province/state combination is not valid
* 22 You have to select a province/state
* 23 The ##Field## field already exists, please choose a different one
* 24 A canadian postal code should be seven characters including a space in the middle.
*    It has to be a letter, followed by a digit, a letter, a space, a digit, a letter and a digit.
* 25 A US zip code should be five digits or 10 characters starting with five digits,
*    followed by a dash and four digits
* 26 The ##Field## field should be between ##NumericLo## and ##NumericHi##
* 27 The ##Field## field should have at least ##MinLength## characters
* 28 The ##Field## field cannot contain the ##Character## character
* 29 The ##Field## field cannot contain the ##String## word
* 30 The ##Field## field cannot be equal to ##String##
* 31 The ##Field## field is invalid
* 32 The ##Field## field cannot content an initial
* 33 The ##Field## field should be zero or between ##NumericLo## and ##NumericHi##
* 34 The ##Field## field should not be longer than ##MaxLength## characters
* 35 The ##Field## field cannot contain a character

* Text in English
This.aMessage[1,1]='The IMG, TABLE, HTML, BODY, FORM, BLINK, OBJECT, SERVER, STYLE, IFRAME, '+;
 'TR, TD, SELECT, HR, DIV, SPAN, FRAME, BUTTON, INPUT, TEXTAREA, TBODY, MARQUEE, TITLE tags or '+;
 'any scripting are only allowed when between a PRE tag. If you wish to include some code, just '+;
 'include them between the PRE tag. The application will make sure your '+;
 'code is well formatted.'
This.aMessage[2,1]='For a A HREF tag, you cannot include a style attribute. '+;
 'There is also no need to put a TARGET as this will be done automatically by the application.'
This.aMessage[3,1]='You cannot include a style attribute inside a tag.'
This.aMessage[4,1]='For the FONT tag, only the SIZE, COLOR and FACE attributes are supported.'
This.aMessage[5,1]='The content you submitted contains some obscenities. '+;
 'Please, adjust it in order to assure that its content reflects a proper content.'
This.aMessage[6,1]='The ##Field## field cannot be empty.'
This.aMessage[7,1]='Please, do not put the content of the ##Field## field in uppercase.'
This.aMessage[8,1]='The email cannot be blank.'
This.aMessage[9,1]='You need to have at least one dot in your email.'
This.aMessage[10,1]='You need to have at least one @ in your email.'
This.aMessage[11,1]='You cannot have an email starting with @.'
This.aMessage[12,1]='The last character of your email cannot be a dot.'
This.aMessage[13,1]='You cannot have more than one @ in your email.'
This.aMessage[14,1]='You cannot have a space in the email.'
This.aMessage[15,1]='You cannot have a comma in the email.'
This.aMessage[16,1]='The email is not valid.'
This.aMessage[17,1]='You need to have at least five characters in your email.'
This.aMessage[18,1]='After the last dot, you cannot have a number in your email.'
This.aMessage[19,1]='You cannot have a dot after the @ in the email.'
This.aMessage[20,1]='You cannot have a semi colon in the email.'
This.aMessage[21,1]='The country and province/state combination is not valid.'
This.aMessage[22,1]='You have to select a province/state.'
This.aMessage[23,1]='The ##Field## field already exists, please choose a different one.'
This.aMessage[24,1]='A canadian postal code should be seven characters including a '+;
 'space in the middle. It has to be a letter, followed by a digit, a letter, a space, '+;
 'a digit, a letter and a digit.'
This.aMessage[25,1]='A US zip code should be five digits or 10 characters starting '+;
 'with five digits, followed by a dash and four digits.'
This.aMessage[26,1]='The value of the ##Field## field should be between ##NumericLo## and ##NumericHi##'
This.aMessage[27,1]='The value of the ##Field## field should have at least ##MinLength## characters'
This.aMessage[28,1]='The ##Field## field cannot contain the ##Character## character'
This.aMessage[29,1]='The ##Field## field cannot contain the ##String## word'
This.aMessage[30,1]='The ##Field## field cannot be equal to ##String##'
This.aMessage[31,1]='The value of the ##Field## field is invalid'
This.aMessage[32,1]='The value of the ##Field## field cannot content an initial'
This.aMessage[33,1]='The value of the ##Field## field should be zero or between '+;
 '##NumericLo## and ##NumericHi##'
This.aMessage[34,1]='The value of the ##Field## field should not be longer than '+;
 '##MaxLength## characters'
This.aMessage[35,1]='The ##Field## field cannot contain a character.'

* Text in French
This.aMessage[1,2]="Les tag IMG, TABLE, HTML, BODY, FORM, BLINK, OBJECT, SERVER, STYLE, "+;
 "IFRAME, TR, TD, SELECT, HR, DIV, SPAN, FRAME, BUTTON, INPUT, TEXTAREA, TBODY, MARQUEE, "+;
 "TITLE ou du scripting ne sont permis que lorsqu'ils sont dans un tag PRE. "+;
 "Si vous voulez inclure du code, vous pouvez l'inclure à l'intérieur du tag "+;
 "PRE. L'application va faire en sorte que votre code "+;
 "soit bien formatté."
This.aMessage[2,2]="Pour le tag A HREF, vous ne pouvez pas inclure un attribut de "+;
 "style. Il n'est aussi pas nécessaire d'inclure un TARGET car l'application "+;
 "le fera pour vous."
This.aMessage[3,2]='Vous ne pouvez pas inclure un tag avec des attributs de style.'
This.aMessage[4,2]='Pour le tag FONT, seul les attributs SIZE, COLOR et FACE sont supportés.'
This.aMessage[5,2]="Le contenu que vous avez envoyé contient des mots vulgaires. "+;
 "S.v.p., veuillez l'ajuster afin d'assurer que son contenu soit respectable."
This.aMessage[6,2]='Le champ ##Field## ne peut être vide.'
This.aMessage[7,2]='S.v.p., ne pas écrire le contenu du champ champ ##Field## en majuscule.'
This.aMessage[8,2]="L'adresse de courrier électronique ne peut être vide."
This.aMessage[9,2]='Vous devez avoir au moins un point dans votre adresse de courrier électronique.'
This.aMessage[10,2]='Vous devez avoir au moins un @ dans votre adresse de courrier électronique.'
This.aMessage[11,2]='Vous ne pouvez avoir une adresse de courrier électronique débutant par @.'
This.aMessage[12,2]='Le dernier caractètre de votre adresse de courrier '+;
 'électronique ne peut être un point.'
This.aMessage[13,2]="Vous ne pouvez avoir plus d'un @ dans votre adresse de courrier électronique."
This.aMessage[14,2]='Vous ne pouvez pas avoir un espace dans votre adresse de courrier électronique.'
This.aMessage[15,2]='Vous ne pouvez pas avoir une virgule dans votre adresse de courrier électronique.'
This.aMessage[16,2]="L'adresse de courrier électronique n'est pas valide."
This.aMessage[17,2]='Vous devez avoir au moins 5 caractères dans votre adresse de '+;
 'courrier électronique.'
This.aMessage[18,2]='Après le dernier point, vous ne pouvez avoir un chiffre dans '+;
 'votre adresse de courrier électronique.'
This.aMessage[19,2]='Vous ne pouvez pas avoir un point après le @ dans votre adresse '+;
 'de courrier électronique.'
This.aMessage[20,2]='Vous ne pouvez pas avoir un point virgule dans votre adresse de '+;
 'courrier électronique.'
This.aMessage[21,2]="La combinaison pays et province/état n'est pas valide."
This.aMessage[22,2]='Vous devez sélectionner une province/état.'
This.aMessage[23,2]="Le champ ##nom d'usager## existe déjà. S.V.P., veuillez en choisir un autre."
This.aMessage[24,2]="Un code postal canadian doit contenir sept caractères et avoir "+;
 "un espace au milieu. Il doit commencer par une lettre, suivi d'un chiffre, une "+;
 "lettre, un espace, un chiffre, une lettre et un chiffre."
This.aMessage[25,2]="Un zip code américain doit être de cinq chiffres ou de 11 "+;
 "caractères commencant pour cinq chiffres, suivi d'un tiret et de quatre chiffres."
This.aMessage[26,2]='La valeur du champ ##Field## doit être entre ##NumericLo## et ##NumericHi##'
This.aMessage[27,2]='La valeur du champ ##Field## doit contenir au moins ##MinLength## caractères'
This.aMessage[28,2]='La valeur du champ ##Field## ne peut contenir le caractère ##Character##'
This.aMessage[29,2]='La valeur du champ ##Field## ne peut contenir le mot ##String##'
This.aMessage[30,2]='La valeur du champ ##Field## ne peut être égale à ##String##'
This.aMessage[31,2]='La valeur du champ ##Field## est invalide'
This.aMessage[32,2]='La valeur du champ ##Field## ne peut contenir une initiale'
This.aMessage[33,2]='La valeur du champ ##Field## doit être zéro ou entre '+;
 '##NumericLo## et ##NumericHi##'
This.aMessage[34,2]='La valeur du champ ##Field## ne doit pas dépasser ##MaxLength## '+;
 'caractères'
This.aMessage[35,2]='Le champ ##Field## ne peut contenir de caractère.'

* Text in Portuguese
This.aMessage[1,3]='As tags IMG, TABLE, HTML, BODY, FORM, BLINK, OBJECT, SERVER, STYLE, '+;
 'IFRAME, TR, TD, SELECT, HR, DIV, SPAN, FRAME, BUTTON, INPUT, TEXTAREA, TBODY, MARQUEE, '+;
 'TITLE ou qualquer script não são permitidos quando estiverem entre a tag PRE. '+;
 'Se você deseja incluir algum código em sua, apenas inclua-o entre tag '+;
 'PRE. O parser do application cuidará para que seu código seja '+;
 'bem formatado.'
This.aMessage[2,3]='Para uma tag A HREF, você não pode incluir um atributo de estilo. '+;
 'Também não há a necessidade de um TARGET visto que isto é feito automaticamente '+;
 'pelo application.'
This.aMessage[3,3]='Você não pode incluir um atributo de estilo dentro de uma tag.'
This.aMessage[4,3]='Para a tag FONT, somente são suportados os atributos SIZE, COLOR e FACE.'
This.aMessage[5,3]='Sua mensagem contém algum obscenidade. Por favor, ajuste-a '+;
 'para assegurar que reflita um conteúdo apropriado.'
This.aMessage[6,3]='The ##Field## field cannot be empty.'
This.aMessage[7,3]='Please, do not put the content of the ##Field## field in uppercase.'
This.aMessage[8,3]='O email não pode estar em branco.'
This.aMessage[9,3]='Você deve ter pelo menos um ponto em seu email.'
This.aMessage[10,3]='Você deve ter pelo menos um @ em seu email.'
This.aMessage[11,3]='O email não pode começar com @.'
This.aMessage[12,3]='O último caracter do email não pode ser um ponto.'
This.aMessage[13,3]='Você não pode ter mais que uma @ no seu email.'
This.aMessage[14,3]='Você não pode ter espaços em seu email.'
This.aMessage[15,3]='Você não pode ter vírgula em seu email.'
This.aMessage[16,3]='O email é inválido.'
This.aMessage[17,3]='Você deve ter pelo menos cinco caracteres em seu email.'
This.aMessage[18,3]='Após o último ponto, você não pode ter números em seu email.'
This.aMessage[19,3]='No seu email, você não pode ter um ponto logo após o @.'
This.aMessage[20,3]='Você não pode ter ponto-e-vírgula no seu email.'
This.aMessage[21,3]='The country and province/state combination is not valid.'
This.aMessage[22,3]='You have to select a province/state.'
This.aMessage[23,3]='The ##Field## field already exists, please choose a different one.'
This.aMessage[24,3]='A canadian postal code should be seven characters including a '+;
 'space in the middle. It has to be a letter, followed by a digit, a letter, a space, '+;
 'a digit, a letter and a digit.'
This.aMessage[25,3]='A US zip code should be five digits or 10 characters starting '+;
 'with five digits, followed by a dash and four digits.'
This.aMessage[26,3]='The value of the ##Field## field should be between ##NumericLo## and ##NumericHi##'
This.aMessage[27,3]='The value of the ##Field## field should have at least ##MinLength## characters'
This.aMessage[28,3]='The ##Field## field cannot contain the ##Character## character'
This.aMessage[29,3]='The ##Field## field cannot contain the ##String## word'
This.aMessage[30,3]='The ##Field## field cannot be equal to ##String##'
This.aMessage[31,3]='The value of the ##Field## field is invalid'
This.aMessage[32,3]='The value of the ##Field## field cannot content an initial'
This.aMessage[33,3]='The value of the ##Field## field should be zero or between '+;
 '##NumericLo## and ##NumericHi##'
This.aMessage[34,3]='The value of the ##Field## field should not be longer than '+;
 '##MaxLength## characters'
This.aMessage[35,3]='The ##Field## field cannot contain a character.'

* Text in Spanish
This.aMessage[1,4]='Los tags IMG, TABLE, HTML, BODY, FORM, BLINK, OBJECT, SERVER, STYLE, '+;
 'IFRAME, TR, TD, SELECT, HR, DIV, SPAN, FRAME, BUTTON, INPUT, TEXTAREA, TBODY ,MARQUEE y '+;
 'TITLE sólo están permitidos dentro del tag PRE. Si desea incluír código en su, '+;
 'hágalo encerrado en los tag PRE. El parser de application se '+;
 'asegurará que su código quede bien formateado.'
This.aMessage[2,4]='Para el tag A HREF, no puede incluir atributos de estilo. '+;
 'No hay necesidad de poner TARGET ya que esto es manejado automáticamente por '+;
 'application.'
This.aMessage[3,4]='No puede incluir atributos de estilo.'
This.aMessage[4,4]='Para el tag FONT sólo se soportan los atributos SIZE, COLOR y FACE.'
This.aMessage[5,4]='Su mensaje contiene obscenidades. Por favor, modifíquelo para '+;
 'asegurarse que refleje un contenido apropiado.'
This.aMessage[6,4]='The ##Field## field cannot be empty.'
This.aMessage[7,4]='Please, do not put the content of the ##Field## field in uppercase.'
This.aMessage[8,4]='La dirección de email no puede estar en blanco.'
This.aMessage[9,4]='Debe tener al menos un punto en su email.'
This.aMessage[10,4]='Debe tener al menos una @ en su email.'
This.aMessage[11,4]='El email no puede comenzar con @.'
This.aMessage[12,4]='El último caracter de la dirección de email no puede ser un punto.'
This.aMessage[13,4]='No puede tener más de una @ en su email.'
This.aMessage[14,4]='No puede haber espacios en el email.'
This.aMessage[15,4]='No puede tener una coma en su email.'
This.aMessage[16,4]='La dirección de correo electrónico no es válida.'
This.aMessage[17,4]='Tiene que tener al menos cinco caracteres en su email.'
This.aMessage[18,4]='Luego del último punto no puede tener números en su email.'
This.aMessage[19,4]='No puede tener un punto justo detrás de @ en su email.'
This.aMessage[20,4]='No puede tener un punto y coma en su email.'
This.aMessage[21,4]='The country and province/state combination is not valid.'
This.aMessage[22,4]='You have to select a province/state.'
This.aMessage[23,4]='The ##Field## field already exists, please choose a different one.'
This.aMessage[24,4]='A canadian postal code should be seven characters including a '+;
 'space in the middle. It has to be a letter, followed by a digit, a letter, a space, '+;
 'a digit, a letter and a digit.'
This.aMessage[25,4]='A US zip code should be five digits or 10 characters starting '+;
 'with five digits, followed by a dash and four digits.'
This.aMessage[26,4]='The value of the ##Field## field should be between ##NumericLo## and ##NumericHi##'
This.aMessage[27,4]='The value of the ##Field## field should have at least ##MinLength## characters'
This.aMessage[28,4]='The ##Field## field cannot contain the ##Character## character'
This.aMessage[29,4]='The ##Field## field cannot contain the ##String## word'
This.aMessage[30,4]='The ##Field## field cannot be equal to ##String##'
This.aMessage[31,4]='The value of the ##Field## field is invalid'
This.aMessage[32,4]='The value of the ##Field## field cannot content an initial'
This.aMessage[33,4]='The value of the ##Field## field should be zero or between '+;
 '##NumericLo## and ##NumericHi##'
This.aMessage[34,4]='The value of the ##Field## field should not be longer than '+;
 '##MaxLength## characters'
This.aMessage[35,4]='The ##Field## field cannot contain a character.'

The Validate() method

The Validate() method executes the validation rules, based on Field.dbf, on the current record of the given alias. If a validation fails, the cValidationMessage property will be initialized with the proper validation message.

The method uses the cAlias property to locate the table record in Table.dbf, another data dictionary holding information about each table of the application, to get the primary key of the table. This primary key can then be used in the SQL command to extract all the related fields we have for this table in Field.dbf for the validation.

Not all the fields may be present in the HTML form in regards to the list of fields we have in Field.dbf for the validation. A small function IsProcess() is called to make sure the field is present. If it is, the validation will occur.

Another function is used to retrieve the HTML field value of each field present in the form, in regards to Field.dbf for the validation. This function is called GetProcess(). It returns the HTML field value.

Here is the code of the Validate() method:

* Validation from Field.dbf
LOCAL lcAlias,lnLen,lcTable,lnNumero,lcField,lcFieldMemory
LOCAL lnOldSel,lcUpper,lnOldRec,lnNoProvinceValue,lnNoCountryValue,lcRight,lnNumeroTable
LOCAL lcCodeValue,lcCharReject,lnCounter,lcCharacter,lcStringNot,lcString
lnOldSel=SELECT()
lnNumero=This.nNumero
lcAlias=This.cAlias

* Get the table ID
SELECT Table
lnLen=LEN(Table)
lcTable=PADR(lcAlias,lnLen,' ')
SEEK UPPER(lcTable) ORDER TAG Table
lnNumeroTable=Numero

* Get all the fields for this table for the validation
SELECT Field.Field,Field.Mandatory,Field.Uppercase,Field.PutUp,Field.PutUpMod,;
 Field.Email,Field.SaveUpper,Field.SaveLower,Field.URL,Field.Province,Field.Unique,;
 Field.Code,Field.Tag,Field.Style,Field.Obscenity,Field.Anglais,Field.Francais,Field.Admin,;
 Field.Initial,Field.NumericF,Field.NumericHi,Field.NumericLo,Field.MinLength,;
 Field.CharReject,Field.StringNot,Field.ValueNot,Field.SameKey,Field.ZeroValue,;
 Field.MaxLength,Field.CreditCard FROM Field;
 WHERE NoTable=lnNumeroTable INTO CURSOR TempSaveTemplate

* Apply the validation on all fields
SCAN

   * If we are in the administration module and if we bypass the validation
   IF This.lAdmin AND Admin
      LOOP
   ENDIF

   lcField=ALLTRIM(Field)

   * Make sure the field is present on the form
   IF IsProcess(lcField)

      * Type of field
      SELECT(lcAlias)
      This.cType=TYPE(lcField)
      DO CASE
         CASE This.cType='C'
            lcFieldMemory='lc'+lcField
         CASE This.cType='N'
            lcFieldMemory='ln'+lcField
         CASE This.cType='I'
            lcFieldMemory='ln'+lcField
         CASE This.cType='M'
            lcFieldMemory='lm'+lcField
         CASE This.cType='D'
            lcFieldMemory='ld'+lcField
         CASE This.cType='T'
            lcFieldMemory='lt'+lcField
      ENDCASE
      SELECT TempSaveTemplate

      This.xValue=EVALUATE(lcFieldMemory)
      This.cField=ALLTRIM(&gcLanguageText)

      * The content cannot be empty
      IF Mandatory
         DO CASE
            CASE INLIST(This.cType,'C','M')
               IF LEN(This.xValue)=0
                  This.nErrorValidation=6
                  This.cValidationMessage=STRTRAN(This.aMessage[6,gnLanguage],'##Field##',This.cField)
                  SELECT(lnOldSel)
                  RETURN .F.
               ENDIF
            CASE INLIST(This.cType,'N','I')
               IF This.xValue=0
                  This.nErrorValidation=6
                  This.cValidationMessage=STRTRAN(This.aMessage[6,gnLanguage],'##Field##',This.cField)
                  SELECT(lnOldSel)
                  RETURN .F.
               ENDIF
         ENDCASE
      ENDIF

      * No uppercase
      IF Uppercase
         IF LEN(This.xValue)>0
            IF This.xValue=UPPER(This.xValue)

               * This is to avoid something like 11111 to be considered upper
               IF VAL(This.xValue)=0
                  This.nErrorValidation=7
                  This.cValidationMessage=STRTRAN(This.aMessage[7,gnLanguage],'##Field##',This.cField)
                  RETURN .F.
                  SELECT(lnOldSel)
               ENDIF

            ENDIF
         ENDIF
      ENDIF

      * We need to check for valid characters
      * If certain characters are present with a PRE tag, then, that's ok
      IF Tag
         IF NOT This.CheckForValidCharacter()
            This.cValidationMessage=This.ReturnMessage()
            RETURN .F.
         ENDIF
      ENDIF

      * We need to remove any style from specific tags
      IF Style
         IF NOT This.CheckStyle()
            This.cValidationMessage=This.ReturnMessage()
            RETURN .F.
         ENDIF
      ENDIF

      * Verify is the string contains obscenities
      IF Obscenity
         IF NOT This.Obscenity()
            This.cValidationMessage=This.ReturnMessage()
            RETURN .F.
         ENDIF
      ENDIF

      * Province or state
      IF Province
         lnNoProvinceValue=lnNoProvince
         lnNoCountryValue=lnNoCountry

         * If we have additional addresses in the table, we will bind the country
         * field with the same province field
         lcRight=RIGHT(lcField,1)
         IF VAL(lcRight)>0
            lnNoProvinceValue=EVALUATE('lnNoProvinc'+lcRight)
            lnNoCountryValue=EVALUATE('lnNoCountry'+lcRight)
         ENDIF

         * If a province has been selected, it has to match the country
         IF lnNoProvince>0
            SELECT Province
            SEEK lnNoProvince ORDER TAG Numero
            IF lnNoCountry<>NoCountry
               This.nErrorValidation=21
               This.cValidationMessage=This.ReturnMessage()
               SELECT(lnOldSel)
               RETURN .F.
            ENDIF
            SELECT TempSaveTemplate
         ENDIF

         * If US or Canada, a province is required
         IF lnNoProvince=0 AND INLIST(lnNoCountry,1,2)
            This.nErrorValidation=22
            This.cValidationMessage=This.ReturnMessage()
            SELECT(lnOldSel)
            RETURN .F.
         ENDIF

      ENDIF

      * Email
      IF Email
         This.cEmail=This.xValue
         IF NOT This.ValidateEmail()
            SELECT(lnOldSel)
            RETURN .F.
         ENDIF
         This.xValue=LOWER(This.xValue)
      ENDIF

      * Unique
      IF Unique
         SELECT(lcAlias)
         IF lnNumero=0 OR (lnNumero>0 AND UPPER(EVALUATE(lcField))<>UPPER(This.xValue))
            lnLen=LEN(EVALUATE(lcField))
            lcUpper=UPPER(This.xValue)
            lnOldRec=SaveRec()
            SEEK PADR(lcUpper,lnLen,' ') ORDER TAG (lcField)
            IF FOUND()
               This.nErrorValidation=23
               This.cValidationMessage=This.ReturnMessage()
               This.cValidationMessage=STRTRAN(This.cValidationMessage,'##Field##',&gcLanguageText)
               SELECT(lnOldSel)
               RETURN .F.
            ENDIF
            RestRec(lnOldRec)
         ENDIF
         SELECT TempSaveTemplate
      ENDIF

      * Postal code or zip code
      IF Code

         * If we have additional addresses in the table, we will bind the country
         * field with the same province field
         lcRight=RIGHT(lcField,1)
         lcCodeValue=lcCode
         IF VAL(lcRight)>0
            lnNoCountryValue=EVALUATE('lnNoCountry'+lcRight)
            lcCodeValue=EVALUATE('lcCode'+lcRight)
         ENDIF
         
         * Canada
         IF lnNoCountry=1

            * Must be seven characters
            IF LEN(lcCodeValue)<>7
               This.nErrorValidation=24
               This.cValidationMessage=This.ReturnMessage()
               SELECT(lnOldSel)
               RETURN .F.
            ENDIF

            * Must have a space in the middle
            IF SUBSTR(lcCodeValue,4,1)<>' '
               This.nErrorValidation=24
               This.cValidationMessage=This.ReturnMessage()
               SELECT(lnOldSel)
               RETURN .F.
            ENDIF

            * Must be alpha, digit, alpha, space, digit, alpha, digit
            IF NOT ISALPHA(SUBSTR(lcCodeValue,1,1)) AND NOT ISDIGIT(SUBSTR(lcCodeValue,2,1)) AND;
             NOT ISALPHA(SUBSTR(lcCodeValue,3,1)) AND NOT ISDIGIT(SUBSTR(lcCodeValue,5,1)) AND;
             NOT ISALPHA(SUBSTR(lcCodeValue,6,1)) AND NOT ISDIGIT(SUBSTR(lcCodeValue,7,1))
               This.nErrorValidation=24
               This.cValidationMessage=This.ReturnMessage()
               SELECT(lnOldSel)
               RETURN .F.
            ENDIF

         ENDIF

         * United States
         IF lnNoCountry=2

            * Must be five digits or five digits, followed by a dash and four digits
            IF LEN(lcCodeValue)<>5 AND LEN(lcCodeValue)<>10
               This.nErrorValidation=25
               This.cValidationMessage=This.ReturnMessage()
               SELECT(lnOldSel)
               RETURN .F.
            ENDIF

            * Must be all digits
            IF LEN(lcCodeValue)=5
               IF NOT ISDIGIT(SUBSTR(lcCodeValue,1,1)) OR NOT ISDIGIT(SUBSTR(lcCodeValue,2,1)) OR;
                NOT ISDIGIT(SUBSTR(lcCodeValue,3,1)) OR NOT ISDIGIT(SUBSTR(lcCodeValue,4,1)) OR;
                NOT ISDIGIT(SUBSTR(lcCodeValue,5,1))
                  This.nErrorValidation=25
                  This.cValidationMessage=This.ReturnMessage()
                  SELECT(lnOldSel)
                  RETURN .F.
               ENDIF
            ENDIF

            * Must be five digits, followed by a dash and four digits
            IF LEN(lcCodeValue)=10
               IF NOT ISDIGIT(SUBSTR(lcCodeValue,1,1)) OR NOT ISDIGIT(SUBSTR(lcCodeValue,2,1)) OR;
                NOT ISDIGIT(SUBSTR(lcCodeValue,3,1)) OR NOT ISDIGIT(SUBSTR(lcCodeValue,4,1)) OR;
                NOT ISDIGIT(SUBSTR(lcCodeValue,5,1)) OR NOT SUBSTR(lcCodeValue,6,1)='-' OR;
                NOT ISDIGIT(SUBSTR(lcCodeValue,7,1)) OR NOT ISDIGIT(SUBSTR(lcCodeValue,8,1)) OR;
                NOT ISDIGIT(SUBSTR(lcCodeValue,9,1)) OR NOT ISDIGIT(SUBSTR(lcCodeValue,10,1))
                  This.nErrorValidation=25
                  This.cValidationMessage=This.ReturnMessage()
                  SELECT(lnOldSel)
                  RETURN .F.
               ENDIF
            ENDIF

         ENDIF

         This.xValue=UPPER(This.xValue)
         &lcFieldMemory=This.xValue
      ENDIF
 
      * Initial
      IF Initial
         IF LEN(This.xValue)=1 OR (LEN(This.xValue)=2 AND SUBSTR(This.xValue,2,1)='.')
            This.nErrorValidation=32
            This.cValidationMessage=STRTRAN(This.aMessage[32,gnLanguage],'##Field##',This.cField)
            SELECT(lnOldSel)
            RETURN .F.
         ENDIF
      ENDIF

      * Minimum length
      IF MinLength>0
         IF This.cType='M'
            IF LEN(This.xValue)0
         IF This.cType='M'
            IF LEN(This.xValue)>MaxLength
               This.nErrorValidation=34
               This.cValidationMessage=STRTRAN(This.aMessage[34,gnLanguage],'##Field##',This.cField)
               This.cValidationMessage=STRTRAN(This.cValidationMessage,'##MaxLength##',;
                ALLTRIM(STR(MaxLength)))
               SELECT(lnOldSel)
               RETURN .F.
            ENDIF
         ENDIF
      ENDIF

      * Characters rejected
      lcCharReject=ALLTRIM(CharReject)
      IF LEN(lcCharReject)>0
         IF INLIST(This.cType,'C','M')
            FOR lnCounter=1 TO LEN(lcCharReject)
               lcCharacter=SUBSTR(lcCharReject,lnCounter,1)
               IF lcCharacter$This.xValue
                  This.nErrorValidation=28
                  This.cValidationMessage=STRTRAN(This.aMessage[28,gnLanguage],'##Field##',This.cField)
                  This.cValidationMessage=STRTRAN(This.cValidationMessage,'##Character##',lcCharacter)
                  SELECT(lnOldSel)
                  RETURN .F.
               ENDIF
            NEXT
         ENDIF
      ENDIF

      * Strings rejected
      lcStringNot=ALLTRIM(StringNot)
      IF LEN(lcStringNot)>0
         IF INLIST(This.cType,'C','M')
            FOR lnCounter=1 TO ParmCnt(lcStringNot)
               lcString=GetParm(lcStringNot,lnCounter)
               IF UPPER(lcString)$UPPER(This.xValue)
                  This.nErrorValidation=29
                  This.cValidationMessage=STRTRAN(This.aMessage[29,gnLanguage],'##Field##',This.cField)
                  This.cValidationMessage=STRTRAN(This.cValidationMessage,'##String##',lcString)
                  SELECT(lnOldSel)
                  RETURN .F.
               ENDIF
            NEXT
         ENDIF
      ENDIF

      * Values rejected
      lcStringNot=ALLTRIM(ValueNot)
      IF LEN(lcStringNot)>0
         IF INLIST(This.cType,'C','M')
            FOR lnCounter=1 TO ParmCnt(lcStringNot)
               lcString=GetParm(lcStringNot,lnCounter)
               IF UPPER(lcString)==UPPER(This.xValue)
                  This.nErrorValidation=30
                  This.cValidationMessage=STRTRAN(This.aMessage[30,gnLanguage],'##Field##',This.cField)
                  This.cValidationMessage=STRTRAN(This.cValidationMessage,'##String##',lcString)
                  SELECT(lnOldSel)
                  RETURN .F.
               ENDIF
            NEXT
         ENDIF
      ENDIF

      * Numeric field
      IF NumericF
         IF ZeroValue
            IF This.xValue<>0 AND NOT (This.xValue>=NumericLo AND This.xValue<=NumericHi)
               This.nErrorValidation=33
               This.cValidationMessage=STRTRAN(This.aMessage[33,gnLanguage],'##Field##',This.cField)
               This.cValidationMessage=STRTRAN(This.cValidationMessage,'##NumericLo##',;
                ALLTRIM(STR(NumericLo)))
               This.cValidationMessage=STRTRAN(This.cValidationMessage,'##NumericHi##',;
                ALLTRIM(STR(NumericHi)))
               RETURN .F.
            ENDIF
            ELSE
            IF NOT (This.xValue>=NumericLo AND This.xValue<=NumericHi)
               This.nErrorValidation=26
               This.cValidationMessage=STRTRAN(This.aMessage[26,gnLanguage],'##Field##',This.cField)
               This.cValidationMessage=STRTRAN(This.cValidationMessage,'##NumericLo##',;
                ALLTRIM(STR(NumericLo)))
               This.cValidationMessage=STRTRAN(This.cValidationMessage,'##NumericHi##',;
                ALLTRIM(STR(NumericHi)))
               RETURN .F.
            ENDIF
         ENDIF
      ENDIF

      * Same key not accepted
      IF SameKey
         IF INLIST(This.cType,'C','M')
            IF LEN(This.xValue)>0 AND UPPER(This.xVvalue)=REPLICATE(SUBSTR(This.xValue,1,1),;
             LEN(This.xValue))
               This.nErrorValidation=31
               This.cValidationMessage=STRTRAN(This.aMessage[31,gnLanguage],'##Field##',This.cField)
               RETURN .F.
            ENDIF
         ENDIF
      ENDIF

      * Credit card
      IF CreditCard

         * Verify for all digits
         FOR lnCounter=1 TO LEN(This.xValue)
            lcString=SUBSTR(This.xValue,lnCounter,1)
            IF NOT ISDIGIT(lcString)
               This.nErrorValidation=35
               This.cValidationMessage=STRTRAN(This.aMessage[35,gnLanguage],'##Field##',This.cField)
               SELECT(lnOldSel)
               RETURN .F.
            ENDIF
         NEXT

         * We need to have at least 15 characters
         IF LEN(This.xValue)<15
            This.nErrorValidation=27
            This.cValidationMessage=STRTRAN(This.aMessage[27,gnLanguage],'##Field##',This.cField)
            This.cValidationMessage=STRTRAN(This.cValidationMessage,'##MinLength##','15')
            SELECT(lnOldSel)
            RETURN .F.
         ENDIF

         * We cannot have more than 16 characters
         IF LEN(This.xValue)>16
            This.nErrorValidation=34
            This.cValidationMessage=STRTRAN(This.aMessage[34,gnLanguage],'##Field##',This.cField)
            This.cValidationMessage=STRTRAN(This.cValidationMessage,'##MaxLength##','16')
            SELECT(lnOldSel)
            RETURN .F.
         ENDIF

      ENDIF

      * PutUp
      IF PutUp
         This.xValue=PutUp(This.xValue)
         &lcFieldMemory=This.xValue
      ENDIF

      * If we save in uppercase
      IF SaveUpper
         This.xValue=UPPER(This.xValue)
         &lcFieldMemory=This.xValue
      ENDIF

      * If we save in lowercase
      IF SaveLower
         This.xValue=LOWER(This.xValue)
         &lcFieldMemory=This.xValue
      ENDIF

      * URL
      IF URL
         This.xValue=AdjustUrl(This.xValue)
         &lcFieldMemory=This.xValue
      ENDIF

   ENDIF

ENDSCAN
SELECT(lnOldSel)
Not all the code is included in this article. However, this should give you a good idea of the potential available for validation when time comes to validate a HTML form by the use of a data dictionary. For the purpose of this article, I only included the code of the Validate() method.

The credit card field

The credit card field in Field.dbf is used to identify a field for credit card validations in regards to the number. For now, what the code does is to make sure we only have digits, that we at least 15 characters and not more than 16. This is good enough to validate the basic rules for Visa, Mastercard and American Express.

With the current design of Field.dbf and this object, it would have been possible to achieve the same without defining any code for this credit card validation. We already have a field in Field.dbf to identify if the field should be numeric, another one for the minimum length and another one for the maximum length. However, it has been done like that for expandability in order to enhance this code later on to insert additional credit card verifications such as a proper credit card number before querying the gateway and some other similar functions. This also allows us to only check one field in Field.dbf to obtain this logic instead of defining multiple ones to achieve the same.

Putting it all together

For our demo, I am including a test form for a data entry of the address of a member. I have included all the fields that appear in the form in Field.dbf. Thus, all of them applies, in this case, to our validation object. They will be processed one by one. As soon as a validation message applies, the process will stop and the control returns to the caller object. The caller object can then use of cValidationMessage property to extract the validation message.

Here is a representation of the Field.dbf table for the fields that belongs to the member table in regards to our test form:

When we update a record, it looks like this:

Our test form looks as follow:

When the user tries to save, if a validation message applies, we can then regenerate the form, with the pre-entered values the user entered, and the validation message. So, from this point, the user can continue to work on the form by adjusting the related field value based on the validation message he received.

In the case of our test form, if the user tries to save, he should get the following message:

Michel Fournier, Level Extreme Inc.
Michel Fournier is a professional, visionary, perfectionist, mostly known for his renowned realizations over the years, designer, architect, owner of the « Level Extreme Platform », formerly known as the « Universal Thread », recognized as one of the longest running Web sites of the planet, also known as a precursor to social networking, product manager, Internet serial entrepreneur, practiced Lean Startup techniques long before they were known, out of the box thinker, using the tenth man rule, specializes in building entire virtual data center solutions, has provided high end IT consulting worldwide, has owned and operated three companies, delivered worldwide renowned e-commerce Web sites, designed and architected two world class top level development frameworks, wrote over 100 IT articles for various sources, presented at user groups, conventions and corporations nationwide as well as in the US, has provided his contribution in political and legal issues to provide a better world, Owner and Senior IT Consultant at Level Extreme Inc., former Architect Software/Application & Project Manager, 7 times Microsoft Most Valued Professional for VB.NET, 7 times Microsoft Most Valued Professional for Visual FoxPro, Developers Choice award for best site at VFP DevCon 2000 Connections in New Orleans, featured in Acadie Nouvelle on October 2003.
More articles from this author
Michel Fournier, February 1, 2007
From the Level Extreme .NET Framework, this small class allows a developer to manipulate easily the content of a directory by the use of a dataset. With the setup of a few properties, a call to the method and the access to the object dataset, you can have access to the file properties of the directo...
Michel Fournier, August 1, 2001
It is interesting to see how something new can evolve. This is the case for the Universal Thread Magazine. We are now at our 3rd issue and we are already overbooked by scheduled articles and hot stuff we have to cover for the upcoming issues. Publishers are sending request for book reviews, wri...
Michel Fournier, October 1, 2002
UTMag/RapoZine team Editors Michel Fournier Claudio Lassala Co-editor Martín Salías Translation coordinators Claudio Lassala Martín Salías Translators Eduardo Vidigal Rodolfo Duarte Fábio Vazquez José Cavalcanti Moacyr Zalcman Fábio Vieira M...
Michel Fournier, May 1, 2006
In this article, Michel Fournier is providing a small introduction to manipulating XML data from VB.NET. The use of XML is now widely used for various purposes such as exchanging data between application, platforms and other environments. XML is a simple and very flexibile text format that can be ma...
Michel Fournier, October 1, 2001
In our daily things we do, sometimes we find ourselves in unexpected situations. Such situations, either in our personal life or from our professional work, require some adjustments in order to walk through them. The ability to take some time to take an overall look of what is happening, apply a bas...
Michel Fournier, March 1, 2007
In this small article, Michel discusses a problem he recently encountered when converting a dataset into XML to be used later on with a XSL transformation to export into an Excel sheet. When null values were present in the dataset, this was creating weird result. This article provides a quite alte...
Michel Fournier, February 1, 2006
This article discusses a simple banner fonctionalities function which can ease the display of banners on Web sites. If your Web site displays banners in GIF, JPG of Flash format, this function could be useful to you.
Michel Fournier, January 1, 2006
There are various ways to authenticate a user to a Web Service. This article discusses one way to do it by the use of Cookies. As it could the case with a Web page sending a cookie to the browser, the same can be used from within a Web Service.
Michel Fournier, February 1, 2006
This article is a follow up on the first part of this article which appeared on our January 2006 issue. In this one, Michel discussed further implementation of getting the authentication from a members table as well as setting up a session per user.
Michel Fournier, December 1, 2003
Visual FoxPro and .NET are two great environments to build business applications with. But, fantastic they are when you combine them together in order to increase the strenght of the flexibility to respond to your client needs. In this article, I will demonstrate a case study in regards to a new ser...
Michel Fournier, December 1, 2002
Over the years, I have been involved in several types of desktop and Web applications. Every time you start a new project, there is always something new you will learn. In this article, I would like to detail some of the issues which are to be considered when delivering a Web based application. Thos...
Michel Fournier, January 1, 2003
This article is a follow-up with more advanced details in regards to the first article of this series in our December issue which included a tip on dealing with stylesheets. This one allows you to customize your HTML code based on the user, assuming each user has some ways to setup some specific sty...
Michel Fournier, March 1, 2003
The first two articles of this series have been published in the issues of December 2002 and January 2003. In this one, I will talk about graphic issues, how to negotiate with a form to launch his transaction to either within the same window or a new one, how to gather values from one page to anothe...
Michel Fournier, April 1, 2003
In this article, I will proceed with considerations about HTTP server variables being received from a browser and about considerations for opening new windows in your Web application. Relying on the protocol or not When it first started, we didn't ask that question to ourselves as to know ...
Michel Fournier, April 1, 2009
This articles describes the use of CDO.Message to gain the ability to retrieve a URL as a MHT file. It also covers an interesting approach to retrieve a URL even if this one requires a login.
Michel Fournier, June 1, 2003
DevTeach was held in Montreal from May 10-13, 2003. It presented a new breed of conference. Sessions included both presentation material and, whenever possible, hands-on training. DevTeach brought under the same roof the best speakers available for .NET, SQL Server and Visual FoxPro as well as Micro...
Michel Fournier, May 1, 2002
The Essential Fox conference was held this weekend in Independence, MO. Once again, the Universal Thread team was on site to do the official coverage of the event. It has been a great success, well planned by Russ Swall, the event owner, and his team and well appreciated by the attendees. A total of...
Michel Fournier, April 1, 2002
UTMag/RapoZine team Editors Michel Fournier Claudio Lassala Translation coordinators Claudio Lassala Martín Salías Translators Eduardo Vidigal Rodolfo Duarte Fábio Vazquez Claudio Rola José Cavalcanti Moacyr Zalcman Ricardo Soares Fábio Vieira ...
Michel Fournier, September 1, 2001
Ever wonder how to successfully and rapidly display HTML lists to your users? Well, we all probably already did. However, its implementation differs a lot from sites to sites as we all have our own different approaches. Delivering Visual FoxPro data to the Web as if you would be in Visual FoxPro is ...
Michel Fournier, November 1, 2001
A lot of things happened recently in the Visual FoxPro world and for related technologies. The Great Lakes Great Database Workshop was being held in Milwaukee from Sunday October 27 to Wednesday October 31. That conference which primaly focused on Visual FoxPro has covered a lot of technologies...
Michel Fournier, November 1, 2002
UTMag/RapoZine team Editors Michel Fournier Claudio Lassala Co-editor Martín Salías Translation coordinators Claudio Lassala Martín Salías Translators Eduardo Vidigal Rodolfo Duarte Fábio Vazquez José Cavalcanti Moacyr Zalcman Fábio Vieira M...
Michel Fournier, January 1, 2003
UTMag/RapoZine team Editors Michel Fournier Claudio Lassala Co-editor Martín Salías Translation coordinators Claudio Lassala Martín Salías Translators Rodolfo Duarte Fábio Vazquez Moacyr Zalcman Martín Salías Antonio Castaño Fabián Belo Rafae...
Michel Fournier, December 1, 2002
UTMag/RapoZine team Editors Michel Fournier Claudio Lassala Co-editor Martín Salías Translation coordinators Claudio Lassala Martín Salías Translators Eduardo Vidigal Rodolfo Duarte Fábio Vazquez José Cavalcanti Moacyr Zalcman Fábio Vieira M...
Michel Fournier, November 1, 2001
I have been following several threads on the Universal Thread recently about FTP from Visual FoxPro. I have used an ActiveX for a while to do such a task. I have found that years after years, the problem is that you have to maintain that ActiveX for your own workstation and for every servers or work...
Michel Fournier, July 1, 2002
UTMag/RapoZine team Editors Michel Fournier Claudio Lassala Co-editor Martín Salías Translation coordinators Claudio Lassala Martín Salías Translators Eduardo Vidigal Rodolfo Duarte Fábio Vazquez José Cavalcanti Moacyr Zalcman Fábio Vieira M...
Michel Fournier, January 1, 2006
With the beginning of the new year, Michel resumes some of the highlights of the Universal Thread and what is coming up for the new season.
Michel Fournier, March 1, 2006
When comes time to display the content of a memo field on a Web page, one common task we have to do is to hyperlink specific content. This article discusses about a technique which can be used to hyperlink various types of links as well as email addresses.
Michel Fournier, April 1, 2009
This article describes some basic techniques to manipulate some images in .NET. It covers image resizing, image cropping and the ability to save an image into a JPG high resolution format.
Michel Fournier, May 1, 2007
This short articles provides an approach of important data from an Excel sheet into your application without having the requirement of having Excel installed on the server.
Michel Fournier, August 1, 2002
UTMag/RapoZine team Editors Michel Fournier Claudio Lassala Co-editor Martín Salías Translation coordinators Claudio Lassala Martín Salías Translators Eduardo Vidigal Rodolfo Duarte Fábio Vazquez José Cavalcanti Moacyr Zalcman Fábio Vieira M...
Michel Fournier, July 1, 2001
Recently, I was having problems while working on several projects on my PC. The problems were happening when I had several applications open at the same time. When the problem occured, I had to reboot my PC and then was able to work for a few hours up to a few days until the next reboot. As I was wo...
Michel Fournier, June 1, 2002
UTMag/RapoZine team Editors Michel Fournier Claudio Lassala Co-editor Martín Salías Translation coordinators Claudio Lassala Martín Salías Translators Eduardo Vidigal Rodolfo Duarte Fábio Vazquez José Cavalcanti Moacyr Zalcman Fábio Vieira M...
Michel Fournier, September 1, 2002
UTMag/RapoZine team Editors Michel Fournier Claudio Lassala Co-editor Martín Salías Translation coordinators Claudio Lassala Martín Salías Translators Eduardo Vidigal Rodolfo Duarte Fábio Vazquez José Cavalcanti Moacyr Zalcman Fábio Vieira M...
Michel Fournier, January 1, 2001
Xitech (Europe) produces tools for the Windows software developer. They specialize in FoxPro Developer tools, data and code recovery and security. In this article, we will see an overview of 5 of their tools. You will find more details about each of them from Xitech documentation. To get Xitech cont...
Michel Fournier, April 1, 2006
This article discusses the ability to use Visual FoxPro to schedule a list of tasks to be executed at specific intervals. While there could be the approach of using the Windows Scheduler to execute those tasks, it is always interesting to be able to control everything from within VFP. A small VFP sc...
Michel Fournier, April 1, 2006
This article describes an overview of sending an email from VB.NET. It covers the basis of creating the email functionality in a class and using an instance of that class to define and send the email. The class includes the ability to send to multiple recipients as well as sending attachments. Sendi...
Michel Fournier, July 1, 2002
This is a follow up on my previous article on using SOAP protocol for authentication that appeared in our December 2001 issue. That article was mentioning the use of the SOAP header for authentication such as being able to identify the user for any upcoming hit to your Web Service as soon as the Log...
Michel Fournier, May 1, 2002
UTMag/RapoZine team Editors Michel Fournier Claudio Lassala Translation coordinators Claudio Lassala Martín Salías Translators Eduardo Vidigal Rodolfo Duarte Fábio Vazquez José Cavalcanti Moacyr Zalcman Fábio Vieira Martín Salías Antonio Castañ...
Michel Fournier, July 1, 2002
From recent discussions I had, with several persons from my team, about common patterns which occur in the evolution of the Universal Thread, I thought it would be nice to write an article about it. Basically, within the evolution of a product, there are some similitudes which are sometimes interest...
Michel Fournier, June 1, 2001
Welcome to our first issue of the Universal Thread Magazine. We kept receiving many requests to have such a media available on the Universal Thread, so we decided to release our first issue this month. Many people have mentioned an interest to either have such a magazine for the pleasure to read abo...
Michel Fournier, December 1, 2001
The Visual FoxPro Zone evolves As many of you may have seen, the Universal Thread Visual FoxPro Zone is evolving quite fast. In the last month, we added new content in it. As usual, the most popular option is the Toledo Wish List. Several entries are created every day. This is the place to co...
Michel Fournier, January 1, 2002
It's January 3rd, 2002, I am writing this editorial at 20h32 EST. The Christmas break is over but was it really a break? More and more, years after years, I keep seeing a lot of persons online during Christmas day or a few minutes before the new year. And, I mean, they are online as per their own ti...
Michel Fournier, January 1, 2004
In December 1993, a great history started when a small Web site known as the Visual FoxPro Yellow Pages started. Basically, a site providing ads for Visual FoxPro developers such as jobs and consulting services. Known also as the first Visual FoxPro site, it has evolved quite fast during the first t...
Michel Fournier, January 1, 2006
In the recent months, I have been involved in settings various projects at client sites, as well as for Level Extreme Web sites, which involved the support of uploading image files from an Internet browser. The process of supporting that capability in your application, either from a desktop of from ...
Michel Fournier, December 1, 2001
The Microsoft SOAP client provides access to any Web Service. Once the object is instantiated and the location of the WSDL file given, you are ready to go to access any method. Thus, based on what is supported by the Web Service, you can query to obtain various types of content such as string and bo...
Michel Fournier, February 1, 2002
On January 15th, 2002, an important joint took place for our magazine. The Universal Thread Magazine and RapoZine magazine, an online magazine available for the Portuguese developers community, joined to create UTMag/RapoZine. Effective from this issue, both magazines will offer the same technical c...
Michel Fournier, July 1, 2002
Show seconds in a readable format If you need to check elapsed time with seconds() or a datetime value, this function allows you to display the elapsed time in a human-readable format, that is, hours:minutes:seconds, instead of the total number of seconds. Just pass a number of seconds as...
Michel Fournier, August 1, 2002
Updating your DLL on IIS This has been a common question in the recent months on the Universal Thread. More and more, developers have the need to use a DLL under IIS. However, the fun part comes when you need to update it. As soon as it kicks in, you can't update your DLL anymore as it re...
Michel Fournier, September 1, 2002
Getting image width and height Probably the most flexible way to extract the width and height of an image is by the use of the image object. All is needed is to load the image in the object and get the values from the Width and Height properties. LOCAL loImage,lnWidth,lnHeight loIma...
Michel Fournier, October 1, 2002
Extracting BMPs from general fields As a complement with last issue's article on image handling, yo can find useful this little function. If you got convinced that using general fields to handle images is a bad idea, you can decided go back to independent image files. But then you'll...
Michel Fournier, November 1, 2002
Use MemLines() to wrap text lines When you need to wrap some text at a given width (say 75 characters per line), you do it easily with: SET MEMOWIDTH TO 75 lcMemo = lcNewMemo = "" _MLINE = 0 FOR i= 1 TO memlines(lcMemo) lcNewMemo = lcNewMemo ; + MLINE(lcMemo,1,_MLINE...
Michel Fournier, June 1, 2001
It was a year ago. The DevConnections team was holding the Visual FoxPro DevCon 2000, the SQL Server Connections and the DevCon 2000 in New Orleans, Louisiana from May 14 to 18, 2000. For the first time, attendees were able to attend sessions from more than one conference at the same time. This offe...
Michel Fournier, September 1, 2001
Is there a speed limit on the Internet? Probably not, because there is so much things we can do in a short time about delivering various type of content to the community. I remember a week ago we shared an idea about helping the promotion of user group activities around the world. A week ago it was ...
Michel Fournier, March 1, 2002
In the last month, we received dozens of emails from satisfied persons in regards for our initiative of opening the magazine and the Universal Thread in general for additional communities such as the Portuguese and Spanish communities. Regulars members of the Universal Thread, new members, Microsoft...